summaryrefslogtreecommitdiff
path: root/arch/s390/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/kernel')
-rw-r--r--arch/s390/kernel/Makefile61
-rw-r--r--arch/s390/kernel/asm-offsets.c163
-rw-r--r--arch/s390/kernel/audit.c78
-rw-r--r--arch/s390/kernel/audit.h15
-rw-r--r--arch/s390/kernel/base.S193
-rw-r--r--arch/s390/kernel/bitmap.c54
-rw-r--r--arch/s390/kernel/compat_audit.c44
-rw-r--r--arch/s390/kernel/compat_exec_domain.c29
-rw-r--r--arch/s390/kernel/compat_linux.c681
-rw-r--r--arch/s390/kernel/compat_linux.h227
-rw-r--r--arch/s390/kernel/compat_ptrace.h63
-rw-r--r--arch/s390/kernel/compat_signal.c597
-rw-r--r--arch/s390/kernel/compat_wrapper.S1649
-rw-r--r--arch/s390/kernel/cpcmd.c125
-rw-r--r--arch/s390/kernel/crash.c16
-rw-r--r--arch/s390/kernel/crash_dump.c442
-rw-r--r--arch/s390/kernel/debug.c1566
-rw-r--r--arch/s390/kernel/diag.c81
-rw-r--r--arch/s390/kernel/dis.c1604
-rw-r--r--arch/s390/kernel/early.c482
-rw-r--r--arch/s390/kernel/ebcdic.c401
-rw-r--r--arch/s390/kernel/entry.S967
-rw-r--r--arch/s390/kernel/entry.h75
-rw-r--r--arch/s390/kernel/entry64.S1030
-rw-r--r--arch/s390/kernel/ftrace.c198
-rw-r--r--arch/s390/kernel/head.S571
-rw-r--r--arch/s390/kernel/head31.S112
-rw-r--r--arch/s390/kernel/head64.S107
-rw-r--r--arch/s390/kernel/head_kdump.S119
-rw-r--r--arch/s390/kernel/init_task.c38
-rw-r--r--arch/s390/kernel/ipl.c2069
-rw-r--r--arch/s390/kernel/irq.c283
-rw-r--r--arch/s390/kernel/jump_label.c70
-rw-r--r--arch/s390/kernel/kprobes.c671
-rw-r--r--arch/s390/kernel/lgr.c200
-rw-r--r--arch/s390/kernel/machine_kexec.c234
-rw-r--r--arch/s390/kernel/mcount.S71
-rw-r--r--arch/s390/kernel/mcount64.S63
-rw-r--r--arch/s390/kernel/mem_detect.c145
-rw-r--r--arch/s390/kernel/module.c395
-rw-r--r--arch/s390/kernel/nmi.c375
-rw-r--r--arch/s390/kernel/os_info.c168
-rw-r--r--arch/s390/kernel/perf_cpum_cf.c690
-rw-r--r--arch/s390/kernel/perf_event.c124
-rw-r--r--arch/s390/kernel/process.c354
-rw-r--r--arch/s390/kernel/processor.c96
-rw-r--r--arch/s390/kernel/ptrace.c1237
-rw-r--r--arch/s390/kernel/reipl.S93
-rw-r--r--arch/s390/kernel/reipl64.S154
-rw-r--r--arch/s390/kernel/relocate_kernel.S119
-rw-r--r--arch/s390/kernel/relocate_kernel64.S122
-rw-r--r--arch/s390/kernel/s390_ksyms.c10
-rw-r--r--arch/s390/kernel/sclp.S353
-rw-r--r--arch/s390/kernel/setup.c1092
-rw-r--r--arch/s390/kernel/signal.c522
-rw-r--r--arch/s390/kernel/smp.c1146
-rw-r--r--arch/s390/kernel/stacktrace.c98
-rw-r--r--arch/s390/kernel/suspend.c182
-rw-r--r--arch/s390/kernel/swsusp_asm64.S285
-rw-r--r--arch/s390/kernel/sys_s390.c151
-rw-r--r--arch/s390/kernel/syscalls.S352
-rw-r--r--arch/s390/kernel/sysinfo.c470
-rw-r--r--arch/s390/kernel/time.c1789
-rw-r--r--arch/s390/kernel/topology.c465
-rw-r--r--arch/s390/kernel/traps.c656
-rw-r--r--arch/s390/kernel/vdso.c333
-rw-r--r--arch/s390/kernel/vdso32/Makefile58
-rw-r--r--arch/s390/kernel/vdso32/clock_getres.S39
-rw-r--r--arch/s390/kernel/vdso32/clock_gettime.S128
-rw-r--r--arch/s390/kernel/vdso32/gettimeofday.S78
-rw-r--r--arch/s390/kernel/vdso32/note.S12
-rw-r--r--arch/s390/kernel/vdso32/vdso32.lds.S138
-rw-r--r--arch/s390/kernel/vdso32/vdso32_wrapper.S14
-rw-r--r--arch/s390/kernel/vdso64/Makefile58
-rw-r--r--arch/s390/kernel/vdso64/clock_getres.S44
-rw-r--r--arch/s390/kernel/vdso64/clock_gettime.S125
-rw-r--r--arch/s390/kernel/vdso64/gettimeofday.S56
-rw-r--r--arch/s390/kernel/vdso64/note.S12
-rw-r--r--arch/s390/kernel/vdso64/vdso64.lds.S138
-rw-r--r--arch/s390/kernel/vdso64/vdso64_wrapper.S14
-rw-r--r--arch/s390/kernel/vmlinux.lds.S96
-rw-r--r--arch/s390/kernel/vtime.c504
82 files changed, 28939 insertions, 0 deletions
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
new file mode 100644
index 00000000..884b18af
--- /dev/null
+++ b/arch/s390/kernel/Makefile
@@ -0,0 +1,61 @@
+#
+# Makefile for the linux kernel.
+#
+
+ifdef CONFIG_FUNCTION_TRACER
+# Don't trace early setup code and tracing code
+CFLAGS_REMOVE_early.o = -pg
+CFLAGS_REMOVE_ftrace.o = -pg
+endif
+
+#
+# Passing null pointers is ok for smp code, since we access the lowcore here.
+#
+CFLAGS_smp.o := -Wno-nonnull
+
+#
+# Pass UTS_MACHINE for user_regset definition
+#
+CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
+
+CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w
+
+obj-y := bitmap.o traps.o time.o process.o base.o early.o setup.o vtime.o \
+ processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o \
+ debug.o irq.o ipl.o dis.o diag.o mem_detect.o sclp.o vdso.o \
+ sysinfo.o jump_label.o lgr.o os_info.o
+
+obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o)
+obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
+
+extra-y += head.o init_task.o vmlinux.lds
+extra-y += $(if $(CONFIG_64BIT),head64.o,head31.o)
+
+obj-$(CONFIG_MODULES) += s390_ksyms.o module.o
+obj-$(CONFIG_SMP) += smp.o
+obj-$(CONFIG_SCHED_BOOK) += topology.o
+obj-$(CONFIG_HIBERNATION) += suspend.o swsusp_asm64.o
+obj-$(CONFIG_AUDIT) += audit.o
+compat-obj-$(CONFIG_AUDIT) += compat_audit.o
+obj-$(CONFIG_COMPAT) += compat_linux.o compat_signal.o \
+ compat_wrapper.o compat_exec_domain.o \
+ $(compat-obj-y)
+
+obj-$(CONFIG_STACKTRACE) += stacktrace.o
+obj-$(CONFIG_KPROBES) += kprobes.o
+obj-$(CONFIG_FUNCTION_TRACER) += $(if $(CONFIG_64BIT),mcount64.o,mcount.o)
+obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
+obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
+obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
+obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
+obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o
+
+# Kexec part
+S390_KEXEC_OBJS := machine_kexec.o crash.o
+S390_KEXEC_OBJS += $(if $(CONFIG_64BIT),relocate_kernel64.o,relocate_kernel.o)
+obj-$(CONFIG_KEXEC) += $(S390_KEXEC_OBJS)
+
+# vdso
+obj-$(CONFIG_64BIT) += vdso64/
+obj-$(CONFIG_32BIT) += vdso32/
+obj-$(CONFIG_COMPAT) += vdso32/
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
new file mode 100644
index 00000000..83e6edf5
--- /dev/null
+++ b/arch/s390/kernel/asm-offsets.c
@@ -0,0 +1,163 @@
+/*
+ * Generate definitions needed by assembly language modules.
+ * This code generates raw asm output which is post-processed to extract
+ * and format the required data.
+ */
+
+#define ASM_OFFSETS_C
+
+#include <linux/kbuild.h>
+#include <linux/sched.h>
+#include <asm/cputime.h>
+#include <asm/timer.h>
+#include <asm/vdso.h>
+#include <asm/pgtable.h>
+
+/*
+ * Make sure that the compiler is new enough. We want a compiler that
+ * is known to work with the "Q" assembler constraint.
+ */
+#if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 3)
+#error Your compiler is too old; please use version 3.3.3 or newer
+#endif
+
+int main(void)
+{
+ DEFINE(__THREAD_info, offsetof(struct task_struct, stack));
+ DEFINE(__THREAD_ksp, offsetof(struct task_struct, thread.ksp));
+ DEFINE(__THREAD_mm_segment, offsetof(struct task_struct, thread.mm_segment));
+ BLANK();
+ DEFINE(__TASK_pid, offsetof(struct task_struct, pid));
+ BLANK();
+ DEFINE(__THREAD_per_cause, offsetof(struct task_struct, thread.per_event.cause));
+ DEFINE(__THREAD_per_address, offsetof(struct task_struct, thread.per_event.address));
+ DEFINE(__THREAD_per_paid, offsetof(struct task_struct, thread.per_event.paid));
+ BLANK();
+ DEFINE(__TI_task, offsetof(struct thread_info, task));
+ DEFINE(__TI_domain, offsetof(struct thread_info, exec_domain));
+ DEFINE(__TI_flags, offsetof(struct thread_info, flags));
+ DEFINE(__TI_cpu, offsetof(struct thread_info, cpu));
+ DEFINE(__TI_precount, offsetof(struct thread_info, preempt_count));
+ DEFINE(__TI_user_timer, offsetof(struct thread_info, user_timer));
+ DEFINE(__TI_system_timer, offsetof(struct thread_info, system_timer));
+ DEFINE(__TI_last_break, offsetof(struct thread_info, last_break));
+ BLANK();
+ DEFINE(__PT_ARGS, offsetof(struct pt_regs, args));
+ DEFINE(__PT_PSW, offsetof(struct pt_regs, psw));
+ DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs));
+ DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2));
+ DEFINE(__PT_INT_CODE, offsetof(struct pt_regs, int_code));
+ DEFINE(__PT_INT_PARM_LONG, offsetof(struct pt_regs, int_parm_long));
+ DEFINE(__PT_SIZE, sizeof(struct pt_regs));
+ BLANK();
+ DEFINE(__SF_BACKCHAIN, offsetof(struct stack_frame, back_chain));
+ DEFINE(__SF_GPRS, offsetof(struct stack_frame, gprs));
+ DEFINE(__SF_EMPTY, offsetof(struct stack_frame, empty1));
+ BLANK();
+ /* timeval/timezone offsets for use by vdso */
+ DEFINE(__VDSO_UPD_COUNT, offsetof(struct vdso_data, tb_update_count));
+ DEFINE(__VDSO_XTIME_STAMP, offsetof(struct vdso_data, xtime_tod_stamp));
+ DEFINE(__VDSO_XTIME_SEC, offsetof(struct vdso_data, xtime_clock_sec));
+ DEFINE(__VDSO_XTIME_NSEC, offsetof(struct vdso_data, xtime_clock_nsec));
+ DEFINE(__VDSO_WTOM_SEC, offsetof(struct vdso_data, wtom_clock_sec));
+ DEFINE(__VDSO_WTOM_NSEC, offsetof(struct vdso_data, wtom_clock_nsec));
+ DEFINE(__VDSO_TIMEZONE, offsetof(struct vdso_data, tz_minuteswest));
+ DEFINE(__VDSO_ECTG_OK, offsetof(struct vdso_data, ectg_available));
+ DEFINE(__VDSO_NTP_MULT, offsetof(struct vdso_data, ntp_mult));
+ DEFINE(__VDSO_ECTG_BASE, offsetof(struct vdso_per_cpu_data, ectg_timer_base));
+ DEFINE(__VDSO_ECTG_USER, offsetof(struct vdso_per_cpu_data, ectg_user_time));
+ /* constants used by the vdso */
+ DEFINE(__CLOCK_REALTIME, CLOCK_REALTIME);
+ DEFINE(__CLOCK_MONOTONIC, CLOCK_MONOTONIC);
+ DEFINE(__CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC);
+ BLANK();
+ /* idle data offsets */
+ DEFINE(__IDLE_ENTER, offsetof(struct s390_idle_data, idle_enter));
+ DEFINE(__IDLE_EXIT, offsetof(struct s390_idle_data, idle_exit));
+ /* vtimer queue offsets */
+ DEFINE(__VQ_IDLE_ENTER, offsetof(struct vtimer_queue, idle_enter));
+ DEFINE(__VQ_IDLE_EXIT, offsetof(struct vtimer_queue, idle_exit));
+ /* lowcore offsets */
+ DEFINE(__LC_EXT_PARAMS, offsetof(struct _lowcore, ext_params));
+ DEFINE(__LC_EXT_CPU_ADDR, offsetof(struct _lowcore, ext_cpu_addr));
+ DEFINE(__LC_EXT_INT_CODE, offsetof(struct _lowcore, ext_int_code));
+ DEFINE(__LC_SVC_ILC, offsetof(struct _lowcore, svc_ilc));
+ DEFINE(__LC_SVC_INT_CODE, offsetof(struct _lowcore, svc_code));
+ DEFINE(__LC_PGM_ILC, offsetof(struct _lowcore, pgm_ilc));
+ DEFINE(__LC_PGM_INT_CODE, offsetof(struct _lowcore, pgm_code));
+ DEFINE(__LC_TRANS_EXC_CODE, offsetof(struct _lowcore, trans_exc_code));
+ DEFINE(__LC_PER_CAUSE, offsetof(struct _lowcore, per_perc_atmid));
+ DEFINE(__LC_PER_ADDRESS, offsetof(struct _lowcore, per_address));
+ DEFINE(__LC_PER_PAID, offsetof(struct _lowcore, per_access_id));
+ DEFINE(__LC_AR_MODE_ID, offsetof(struct _lowcore, ar_access_id));
+ DEFINE(__LC_SUBCHANNEL_ID, offsetof(struct _lowcore, subchannel_id));
+ DEFINE(__LC_SUBCHANNEL_NR, offsetof(struct _lowcore, subchannel_nr));
+ DEFINE(__LC_IO_INT_PARM, offsetof(struct _lowcore, io_int_parm));
+ DEFINE(__LC_IO_INT_WORD, offsetof(struct _lowcore, io_int_word));
+ DEFINE(__LC_STFL_FAC_LIST, offsetof(struct _lowcore, stfl_fac_list));
+ DEFINE(__LC_MCCK_CODE, offsetof(struct _lowcore, mcck_interruption_code));
+ DEFINE(__LC_RST_OLD_PSW, offsetof(struct _lowcore, restart_old_psw));
+ DEFINE(__LC_EXT_OLD_PSW, offsetof(struct _lowcore, external_old_psw));
+ DEFINE(__LC_SVC_OLD_PSW, offsetof(struct _lowcore, svc_old_psw));
+ DEFINE(__LC_PGM_OLD_PSW, offsetof(struct _lowcore, program_old_psw));
+ DEFINE(__LC_MCK_OLD_PSW, offsetof(struct _lowcore, mcck_old_psw));
+ DEFINE(__LC_IO_OLD_PSW, offsetof(struct _lowcore, io_old_psw));
+ DEFINE(__LC_RST_NEW_PSW, offsetof(struct _lowcore, restart_psw));
+ DEFINE(__LC_EXT_NEW_PSW, offsetof(struct _lowcore, external_new_psw));
+ DEFINE(__LC_SVC_NEW_PSW, offsetof(struct _lowcore, svc_new_psw));
+ DEFINE(__LC_PGM_NEW_PSW, offsetof(struct _lowcore, program_new_psw));
+ DEFINE(__LC_MCK_NEW_PSW, offsetof(struct _lowcore, mcck_new_psw));
+ DEFINE(__LC_IO_NEW_PSW, offsetof(struct _lowcore, io_new_psw));
+ BLANK();
+ DEFINE(__LC_SAVE_AREA_SYNC, offsetof(struct _lowcore, save_area_sync));
+ DEFINE(__LC_SAVE_AREA_ASYNC, offsetof(struct _lowcore, save_area_async));
+ DEFINE(__LC_SAVE_AREA_RESTART, offsetof(struct _lowcore, save_area_restart));
+ DEFINE(__LC_RETURN_PSW, offsetof(struct _lowcore, return_psw));
+ DEFINE(__LC_RETURN_MCCK_PSW, offsetof(struct _lowcore, return_mcck_psw));
+ DEFINE(__LC_SYNC_ENTER_TIMER, offsetof(struct _lowcore, sync_enter_timer));
+ DEFINE(__LC_ASYNC_ENTER_TIMER, offsetof(struct _lowcore, async_enter_timer));
+ DEFINE(__LC_MCCK_ENTER_TIMER, offsetof(struct _lowcore, mcck_enter_timer));
+ DEFINE(__LC_EXIT_TIMER, offsetof(struct _lowcore, exit_timer));
+ DEFINE(__LC_USER_TIMER, offsetof(struct _lowcore, user_timer));
+ DEFINE(__LC_SYSTEM_TIMER, offsetof(struct _lowcore, system_timer));
+ DEFINE(__LC_STEAL_TIMER, offsetof(struct _lowcore, steal_timer));
+ DEFINE(__LC_LAST_UPDATE_TIMER, offsetof(struct _lowcore, last_update_timer));
+ DEFINE(__LC_LAST_UPDATE_CLOCK, offsetof(struct _lowcore, last_update_clock));
+ DEFINE(__LC_CURRENT, offsetof(struct _lowcore, current_task));
+ DEFINE(__LC_CURRENT_PID, offsetof(struct _lowcore, current_pid));
+ DEFINE(__LC_THREAD_INFO, offsetof(struct _lowcore, thread_info));
+ DEFINE(__LC_KERNEL_STACK, offsetof(struct _lowcore, kernel_stack));
+ DEFINE(__LC_ASYNC_STACK, offsetof(struct _lowcore, async_stack));
+ DEFINE(__LC_PANIC_STACK, offsetof(struct _lowcore, panic_stack));
+ DEFINE(__LC_RESTART_STACK, offsetof(struct _lowcore, restart_stack));
+ DEFINE(__LC_RESTART_FN, offsetof(struct _lowcore, restart_fn));
+ DEFINE(__LC_USER_ASCE, offsetof(struct _lowcore, user_asce));
+ DEFINE(__LC_INT_CLOCK, offsetof(struct _lowcore, int_clock));
+ DEFINE(__LC_MCCK_CLOCK, offsetof(struct _lowcore, mcck_clock));
+ DEFINE(__LC_MACHINE_FLAGS, offsetof(struct _lowcore, machine_flags));
+ DEFINE(__LC_FTRACE_FUNC, offsetof(struct _lowcore, ftrace_func));
+ DEFINE(__LC_IRB, offsetof(struct _lowcore, irb));
+ DEFINE(__LC_DUMP_REIPL, offsetof(struct _lowcore, ipib));
+ BLANK();
+ DEFINE(__LC_CPU_TIMER_SAVE_AREA, offsetof(struct _lowcore, cpu_timer_save_area));
+ DEFINE(__LC_CLOCK_COMP_SAVE_AREA, offsetof(struct _lowcore, clock_comp_save_area));
+ DEFINE(__LC_PSW_SAVE_AREA, offsetof(struct _lowcore, psw_save_area));
+ DEFINE(__LC_PREFIX_SAVE_AREA, offsetof(struct _lowcore, prefixreg_save_area));
+ DEFINE(__LC_AREGS_SAVE_AREA, offsetof(struct _lowcore, access_regs_save_area));
+ DEFINE(__LC_FPREGS_SAVE_AREA, offsetof(struct _lowcore, floating_pt_save_area));
+ DEFINE(__LC_GPREGS_SAVE_AREA, offsetof(struct _lowcore, gpregs_save_area));
+ DEFINE(__LC_CREGS_SAVE_AREA, offsetof(struct _lowcore, cregs_save_area));
+#ifdef CONFIG_32BIT
+ DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, extended_save_area_addr));
+#else /* CONFIG_32BIT */
+ DEFINE(__LC_EXT_PARAMS2, offsetof(struct _lowcore, ext_params2));
+ DEFINE(SAVE_AREA_BASE, offsetof(struct _lowcore, floating_pt_save_area));
+ DEFINE(__LC_PASTE, offsetof(struct _lowcore, paste));
+ DEFINE(__LC_FP_CREG_SAVE_AREA, offsetof(struct _lowcore, fpt_creg_save_area));
+ DEFINE(__LC_LAST_BREAK, offsetof(struct _lowcore, breaking_event_addr));
+ DEFINE(__LC_VDSO_PER_CPU, offsetof(struct _lowcore, vdso_per_cpu_data));
+ DEFINE(__LC_GMAP, offsetof(struct _lowcore, gmap));
+ DEFINE(__GMAP_ASCE, offsetof(struct gmap, asce));
+#endif /* CONFIG_32BIT */
+ return 0;
+}
diff --git a/arch/s390/kernel/audit.c b/arch/s390/kernel/audit.c
new file mode 100644
index 00000000..f4932c22
--- /dev/null
+++ b/arch/s390/kernel/audit.c
@@ -0,0 +1,78 @@
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/audit.h>
+#include <asm/unistd.h>
+#include "audit.h"
+
+static unsigned dir_class[] = {
+#include <asm-generic/audit_dir_write.h>
+~0U
+};
+
+static unsigned read_class[] = {
+#include <asm-generic/audit_read.h>
+~0U
+};
+
+static unsigned write_class[] = {
+#include <asm-generic/audit_write.h>
+~0U
+};
+
+static unsigned chattr_class[] = {
+#include <asm-generic/audit_change_attr.h>
+~0U
+};
+
+static unsigned signal_class[] = {
+#include <asm-generic/audit_signal.h>
+~0U
+};
+
+int audit_classify_arch(int arch)
+{
+#ifdef CONFIG_COMPAT
+ if (arch == AUDIT_ARCH_S390)
+ return 1;
+#endif
+ return 0;
+}
+
+int audit_classify_syscall(int abi, unsigned syscall)
+{
+#ifdef CONFIG_COMPAT
+ if (abi == AUDIT_ARCH_S390)
+ return s390_classify_syscall(syscall);
+#endif
+ switch(syscall) {
+ case __NR_open:
+ return 2;
+ case __NR_openat:
+ return 3;
+ case __NR_socketcall:
+ return 4;
+ case __NR_execve:
+ return 5;
+ default:
+ return 0;
+ }
+}
+
+static int __init audit_classes_init(void)
+{
+#ifdef CONFIG_COMPAT
+ audit_register_class(AUDIT_CLASS_WRITE_32, s390_write_class);
+ audit_register_class(AUDIT_CLASS_READ_32, s390_read_class);
+ audit_register_class(AUDIT_CLASS_DIR_WRITE_32, s390_dir_class);
+ audit_register_class(AUDIT_CLASS_CHATTR_32, s390_chattr_class);
+ audit_register_class(AUDIT_CLASS_SIGNAL_32, s390_signal_class);
+#endif
+ audit_register_class(AUDIT_CLASS_WRITE, write_class);
+ audit_register_class(AUDIT_CLASS_READ, read_class);
+ audit_register_class(AUDIT_CLASS_DIR_WRITE, dir_class);
+ audit_register_class(AUDIT_CLASS_CHATTR, chattr_class);
+ audit_register_class(AUDIT_CLASS_SIGNAL, signal_class);
+ return 0;
+}
+
+__initcall(audit_classes_init);
diff --git a/arch/s390/kernel/audit.h b/arch/s390/kernel/audit.h
new file mode 100644
index 00000000..12b56f4b
--- /dev/null
+++ b/arch/s390/kernel/audit.h
@@ -0,0 +1,15 @@
+#ifndef __ARCH_S390_KERNEL_AUDIT_H
+#define __ARCH_S390_KERNEL_AUDIT_H
+
+#include <linux/types.h>
+
+#ifdef CONFIG_COMPAT
+extern int s390_classify_syscall(unsigned);
+extern __u32 s390_dir_class[];
+extern __u32 s390_write_class[];
+extern __u32 s390_read_class[];
+extern __u32 s390_chattr_class[];
+extern __u32 s390_signal_class[];
+#endif /* CONFIG_COMPAT */
+
+#endif /* __ARCH_S390_KERNEL_AUDIT_H */
diff --git a/arch/s390/kernel/base.S b/arch/s390/kernel/base.S
new file mode 100644
index 00000000..3aa4d00a
--- /dev/null
+++ b/arch/s390/kernel/base.S
@@ -0,0 +1,193 @@
+/*
+ * arch/s390/kernel/base.S
+ *
+ * Copyright IBM Corp. 2006,2007
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Michael Holzheu <holzheu@de.ibm.com>
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/ptrace.h>
+
+#ifdef CONFIG_64BIT
+
+ENTRY(s390_base_mcck_handler)
+ basr %r13,0
+0: lg %r15,__LC_PANIC_STACK # load panic stack
+ aghi %r15,-STACK_FRAME_OVERHEAD
+ larl %r1,s390_base_mcck_handler_fn
+ lg %r1,0(%r1)
+ ltgr %r1,%r1
+ jz 1f
+ basr %r14,%r1
+1: la %r1,4095
+ lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)
+ lpswe __LC_MCK_OLD_PSW
+
+ .section .bss
+ .align 8
+ .globl s390_base_mcck_handler_fn
+s390_base_mcck_handler_fn:
+ .quad 0
+ .previous
+
+ENTRY(s390_base_ext_handler)
+ stmg %r0,%r15,__LC_SAVE_AREA_ASYNC
+ basr %r13,0
+0: aghi %r15,-STACK_FRAME_OVERHEAD
+ larl %r1,s390_base_ext_handler_fn
+ lg %r1,0(%r1)
+ ltgr %r1,%r1
+ jz 1f
+ basr %r14,%r1
+1: lmg %r0,%r15,__LC_SAVE_AREA_ASYNC
+ ni __LC_EXT_OLD_PSW+1,0xfd # clear wait state bit
+ lpswe __LC_EXT_OLD_PSW
+
+ .section .bss
+ .align 8
+ .globl s390_base_ext_handler_fn
+s390_base_ext_handler_fn:
+ .quad 0
+ .previous
+
+ENTRY(s390_base_pgm_handler)
+ stmg %r0,%r15,__LC_SAVE_AREA_SYNC
+ basr %r13,0
+0: aghi %r15,-STACK_FRAME_OVERHEAD
+ larl %r1,s390_base_pgm_handler_fn
+ lg %r1,0(%r1)
+ ltgr %r1,%r1
+ jz 1f
+ basr %r14,%r1
+ lmg %r0,%r15,__LC_SAVE_AREA_SYNC
+ lpswe __LC_PGM_OLD_PSW
+1: lpswe disabled_wait_psw-0b(%r13)
+
+ .align 8
+disabled_wait_psw:
+ .quad 0x0002000180000000,0x0000000000000000 + s390_base_pgm_handler
+
+ .section .bss
+ .align 8
+ .globl s390_base_pgm_handler_fn
+s390_base_pgm_handler_fn:
+ .quad 0
+ .previous
+
+#
+# Calls diag 308 subcode 1 and continues execution
+#
+# The following conditions must be ensured before calling this function:
+# * Prefix register = 0
+# * Lowcore protection is disabled
+#
+ENTRY(diag308_reset)
+ larl %r4,.Lctlregs # Save control registers
+ stctg %c0,%c15,0(%r4)
+ larl %r4,.Lfpctl # Floating point control register
+ stfpc 0(%r4)
+ larl %r4,.Lrestart_psw # Setup restart PSW at absolute 0
+ lghi %r3,0
+ lg %r4,0(%r4) # Save PSW
+ sturg %r4,%r3 # Use sturg, because of large pages
+ lghi %r1,1
+ diag %r1,%r1,0x308
+.Lrestart_part2:
+ lhi %r0,0 # Load r0 with zero
+ lhi %r1,2 # Use mode 2 = ESAME (dump)
+ sigp %r1,%r0,0x12 # Switch to ESAME mode
+ sam64 # Switch to 64 bit addressing mode
+ larl %r4,.Lctlregs # Restore control registers
+ lctlg %c0,%c15,0(%r4)
+ larl %r4,.Lfpctl # Restore floating point ctl register
+ lfpc 0(%r4)
+ br %r14
+.align 16
+.Lrestart_psw:
+ .long 0x00080000,0x80000000 + .Lrestart_part2
+
+ .section .bss
+.align 8
+.Lctlregs:
+ .rept 16
+ .quad 0
+ .endr
+.Lfpctl:
+ .long 0
+ .previous
+
+#else /* CONFIG_64BIT */
+
+ENTRY(s390_base_mcck_handler)
+ basr %r13,0
+0: l %r15,__LC_PANIC_STACK # load panic stack
+ ahi %r15,-STACK_FRAME_OVERHEAD
+ l %r1,2f-0b(%r13)
+ l %r1,0(%r1)
+ ltr %r1,%r1
+ jz 1f
+ basr %r14,%r1
+1: lm %r0,%r15,__LC_GPREGS_SAVE_AREA
+ lpsw __LC_MCK_OLD_PSW
+
+2: .long s390_base_mcck_handler_fn
+
+ .section .bss
+ .align 4
+ .globl s390_base_mcck_handler_fn
+s390_base_mcck_handler_fn:
+ .long 0
+ .previous
+
+ENTRY(s390_base_ext_handler)
+ stm %r0,%r15,__LC_SAVE_AREA_ASYNC
+ basr %r13,0
+0: ahi %r15,-STACK_FRAME_OVERHEAD
+ l %r1,2f-0b(%r13)
+ l %r1,0(%r1)
+ ltr %r1,%r1
+ jz 1f
+ basr %r14,%r1
+1: lm %r0,%r15,__LC_SAVE_AREA_ASYNC
+ ni __LC_EXT_OLD_PSW+1,0xfd # clear wait state bit
+ lpsw __LC_EXT_OLD_PSW
+
+2: .long s390_base_ext_handler_fn
+
+ .section .bss
+ .align 4
+ .globl s390_base_ext_handler_fn
+s390_base_ext_handler_fn:
+ .long 0
+ .previous
+
+ENTRY(s390_base_pgm_handler)
+ stm %r0,%r15,__LC_SAVE_AREA_SYNC
+ basr %r13,0
+0: ahi %r15,-STACK_FRAME_OVERHEAD
+ l %r1,2f-0b(%r13)
+ l %r1,0(%r1)
+ ltr %r1,%r1
+ jz 1f
+ basr %r14,%r1
+ lm %r0,%r15,__LC_SAVE_AREA_SYNC
+ lpsw __LC_PGM_OLD_PSW
+
+1: lpsw disabled_wait_psw-0b(%r13)
+
+2: .long s390_base_pgm_handler_fn
+
+disabled_wait_psw:
+ .align 8
+ .long 0x000a0000,0x00000000 + s390_base_pgm_handler
+
+ .section .bss
+ .align 4
+ .globl s390_base_pgm_handler_fn
+s390_base_pgm_handler_fn:
+ .long 0
+ .previous
+
+#endif /* CONFIG_64BIT */
diff --git a/arch/s390/kernel/bitmap.c b/arch/s390/kernel/bitmap.c
new file mode 100644
index 00000000..3ae4757b
--- /dev/null
+++ b/arch/s390/kernel/bitmap.c
@@ -0,0 +1,54 @@
+/*
+ * Bitmaps for set_bit, clear_bit, test_and_set_bit, ...
+ * See include/asm/{bitops.h|posix_types.h} for details
+ *
+ * Copyright IBM Corp. 1999,2009
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>,
+ */
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+
+const char _oi_bitmap[] = { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 };
+EXPORT_SYMBOL(_oi_bitmap);
+
+const char _ni_bitmap[] = { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f };
+EXPORT_SYMBOL(_ni_bitmap);
+
+const char _zb_findmap[] = {
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,
+ 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8 };
+EXPORT_SYMBOL(_zb_findmap);
+
+const char _sb_findmap[] = {
+ 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
+ 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 };
+EXPORT_SYMBOL(_sb_findmap);
diff --git a/arch/s390/kernel/compat_audit.c b/arch/s390/kernel/compat_audit.c
new file mode 100644
index 00000000..d6487bf8
--- /dev/null
+++ b/arch/s390/kernel/compat_audit.c
@@ -0,0 +1,44 @@
+#undef __s390x__
+#include <asm/unistd.h>
+#include "audit.h"
+
+unsigned s390_dir_class[] = {
+#include <asm-generic/audit_dir_write.h>
+~0U
+};
+
+unsigned s390_chattr_class[] = {
+#include <asm-generic/audit_change_attr.h>
+~0U
+};
+
+unsigned s390_write_class[] = {
+#include <asm-generic/audit_write.h>
+~0U
+};
+
+unsigned s390_read_class[] = {
+#include <asm-generic/audit_read.h>
+~0U
+};
+
+unsigned s390_signal_class[] = {
+#include <asm-generic/audit_signal.h>
+~0U
+};
+
+int s390_classify_syscall(unsigned syscall)
+{
+ switch(syscall) {
+ case __NR_open:
+ return 2;
+ case __NR_openat:
+ return 3;
+ case __NR_socketcall:
+ return 4;
+ case __NR_execve:
+ return 5;
+ default:
+ return 1;
+ }
+}
diff --git a/arch/s390/kernel/compat_exec_domain.c b/arch/s390/kernel/compat_exec_domain.c
new file mode 100644
index 00000000..914d4944
--- /dev/null
+++ b/arch/s390/kernel/compat_exec_domain.c
@@ -0,0 +1,29 @@
+/*
+ * Support for 32-bit Linux for S390 personality.
+ *
+ * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Gerhard Tonn (ton@de.ibm.com)
+ *
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/personality.h>
+#include <linux/sched.h>
+
+static struct exec_domain s390_exec_domain;
+
+static int __init s390_init (void)
+{
+ s390_exec_domain.name = "Linux/s390";
+ s390_exec_domain.handler = NULL;
+ s390_exec_domain.pers_low = PER_LINUX32;
+ s390_exec_domain.pers_high = PER_LINUX32;
+ s390_exec_domain.signal_map = default_exec_domain.signal_map;
+ s390_exec_domain.signal_invmap = default_exec_domain.signal_invmap;
+ register_exec_domain(&s390_exec_domain);
+ return 0;
+}
+
+__initcall(s390_init);
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
new file mode 100644
index 00000000..ab64bdba
--- /dev/null
+++ b/arch/s390/kernel/compat_linux.c
@@ -0,0 +1,681 @@
+/*
+ * arch/s390x/kernel/linux32.c
+ *
+ * S390 version
+ * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ * Gerhard Tonn (ton@de.ibm.com)
+ * Thomas Spatzier (tspat@de.ibm.com)
+ *
+ * Conversion between 31bit and 64bit native syscalls.
+ *
+ * Heavily inspired by the 32-bit Sparc compat code which is
+ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/file.h>
+#include <linux/signal.h>
+#include <linux/resource.h>
+#include <linux/times.h>
+#include <linux/smp.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/uio.h>
+#include <linux/quota.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/personality.h>
+#include <linux/stat.h>
+#include <linux/filter.h>
+#include <linux/highmem.h>
+#include <linux/highuid.h>
+#include <linux/mman.h>
+#include <linux/ipv6.h>
+#include <linux/in.h>
+#include <linux/icmpv6.h>
+#include <linux/syscalls.h>
+#include <linux/sysctl.h>
+#include <linux/binfmts.h>
+#include <linux/capability.h>
+#include <linux/compat.h>
+#include <linux/vfs.h>
+#include <linux/ptrace.h>
+#include <linux/fadvise.h>
+#include <linux/ipc.h>
+#include <linux/slab.h>
+
+#include <asm/types.h>
+#include <asm/uaccess.h>
+
+#include <net/scm.h>
+#include <net/sock.h>
+
+#include "compat_linux.h"
+
+u32 psw32_user_bits = PSW32_MASK_DAT | PSW32_MASK_IO | PSW32_MASK_EXT |
+ PSW32_DEFAULT_KEY | PSW32_MASK_BASE | PSW32_MASK_MCHECK |
+ PSW32_MASK_PSTATE | PSW32_ASC_HOME;
+
+/* For this source file, we want overflow handling. */
+
+#undef high2lowuid
+#undef high2lowgid
+#undef low2highuid
+#undef low2highgid
+#undef SET_UID16
+#undef SET_GID16
+#undef NEW_TO_OLD_UID
+#undef NEW_TO_OLD_GID
+#undef SET_OLDSTAT_UID
+#undef SET_OLDSTAT_GID
+#undef SET_STAT_UID
+#undef SET_STAT_GID
+
+#define high2lowuid(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid)
+#define high2lowgid(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid)
+#define low2highuid(uid) ((uid) == (u16)-1) ? (uid_t)-1 : (uid_t)(uid)
+#define low2highgid(gid) ((gid) == (u16)-1) ? (gid_t)-1 : (gid_t)(gid)
+#define SET_UID16(var, uid) var = high2lowuid(uid)
+#define SET_GID16(var, gid) var = high2lowgid(gid)
+#define NEW_TO_OLD_UID(uid) high2lowuid(uid)
+#define NEW_TO_OLD_GID(gid) high2lowgid(gid)
+#define SET_OLDSTAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid)
+#define SET_OLDSTAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid)
+#define SET_STAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid)
+#define SET_STAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid)
+
+asmlinkage long sys32_chown16(const char __user * filename, u16 user, u16 group)
+{
+ return sys_chown(filename, low2highuid(user), low2highgid(group));
+}
+
+asmlinkage long sys32_lchown16(const char __user * filename, u16 user, u16 group)
+{
+ return sys_lchown(filename, low2highuid(user), low2highgid(group));
+}
+
+asmlinkage long sys32_fchown16(unsigned int fd, u16 user, u16 group)
+{
+ return sys_fchown(fd, low2highuid(user), low2highgid(group));
+}
+
+asmlinkage long sys32_setregid16(u16 rgid, u16 egid)
+{
+ return sys_setregid(low2highgid(rgid), low2highgid(egid));
+}
+
+asmlinkage long sys32_setgid16(u16 gid)
+{
+ return sys_setgid((gid_t)gid);
+}
+
+asmlinkage long sys32_setreuid16(u16 ruid, u16 euid)
+{
+ return sys_setreuid(low2highuid(ruid), low2highuid(euid));
+}
+
+asmlinkage long sys32_setuid16(u16 uid)
+{
+ return sys_setuid((uid_t)uid);
+}
+
+asmlinkage long sys32_setresuid16(u16 ruid, u16 euid, u16 suid)
+{
+ return sys_setresuid(low2highuid(ruid), low2highuid(euid),
+ low2highuid(suid));
+}
+
+asmlinkage long sys32_getresuid16(u16 __user *ruid, u16 __user *euid, u16 __user *suid)
+{
+ int retval;
+
+ if (!(retval = put_user(high2lowuid(current->cred->uid), ruid)) &&
+ !(retval = put_user(high2lowuid(current->cred->euid), euid)))
+ retval = put_user(high2lowuid(current->cred->suid), suid);
+
+ return retval;
+}
+
+asmlinkage long sys32_setresgid16(u16 rgid, u16 egid, u16 sgid)
+{
+ return sys_setresgid(low2highgid(rgid), low2highgid(egid),
+ low2highgid(sgid));
+}
+
+asmlinkage long sys32_getresgid16(u16 __user *rgid, u16 __user *egid, u16 __user *sgid)
+{
+ int retval;
+
+ if (!(retval = put_user(high2lowgid(current->cred->gid), rgid)) &&
+ !(retval = put_user(high2lowgid(current->cred->egid), egid)))
+ retval = put_user(high2lowgid(current->cred->sgid), sgid);
+
+ return retval;
+}
+
+asmlinkage long sys32_setfsuid16(u16 uid)
+{
+ return sys_setfsuid((uid_t)uid);
+}
+
+asmlinkage long sys32_setfsgid16(u16 gid)
+{
+ return sys_setfsgid((gid_t)gid);
+}
+
+static int groups16_to_user(u16 __user *grouplist, struct group_info *group_info)
+{
+ int i;
+ u16 group;
+
+ for (i = 0; i < group_info->ngroups; i++) {
+ group = (u16)GROUP_AT(group_info, i);
+ if (put_user(group, grouplist+i))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int groups16_from_user(struct group_info *group_info, u16 __user *grouplist)
+{
+ int i;
+ u16 group;
+
+ for (i = 0; i < group_info->ngroups; i++) {
+ if (get_user(group, grouplist+i))
+ return -EFAULT;
+ GROUP_AT(group_info, i) = (gid_t)group;
+ }
+
+ return 0;
+}
+
+asmlinkage long sys32_getgroups16(int gidsetsize, u16 __user *grouplist)
+{
+ int i;
+
+ if (gidsetsize < 0)
+ return -EINVAL;
+
+ get_group_info(current->cred->group_info);
+ i = current->cred->group_info->ngroups;
+ if (gidsetsize) {
+ if (i > gidsetsize) {
+ i = -EINVAL;
+ goto out;
+ }
+ if (groups16_to_user(grouplist, current->cred->group_info)) {
+ i = -EFAULT;
+ goto out;
+ }
+ }
+out:
+ put_group_info(current->cred->group_info);
+ return i;
+}
+
+asmlinkage long sys32_setgroups16(int gidsetsize, u16 __user *grouplist)
+{
+ struct group_info *group_info;
+ int retval;
+
+ if (!capable(CAP_SETGID))
+ return -EPERM;
+ if ((unsigned)gidsetsize > NGROUPS_MAX)
+ return -EINVAL;
+
+ group_info = groups_alloc(gidsetsize);
+ if (!group_info)
+ return -ENOMEM;
+ retval = groups16_from_user(group_info, grouplist);
+ if (retval) {
+ put_group_info(group_info);
+ return retval;
+ }
+
+ retval = set_current_groups(group_info);
+ put_group_info(group_info);
+
+ return retval;
+}
+
+asmlinkage long sys32_getuid16(void)
+{
+ return high2lowuid(current->cred->uid);
+}
+
+asmlinkage long sys32_geteuid16(void)
+{
+ return high2lowuid(current->cred->euid);
+}
+
+asmlinkage long sys32_getgid16(void)
+{
+ return high2lowgid(current->cred->gid);
+}
+
+asmlinkage long sys32_getegid16(void)
+{
+ return high2lowgid(current->cred->egid);
+}
+
+/*
+ * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation.
+ *
+ * This is really horribly ugly.
+ */
+#ifdef CONFIG_SYSVIPC
+asmlinkage long sys32_ipc(u32 call, int first, int second, int third, u32 ptr)
+{
+ if (call >> 16) /* hack for backward compatibility */
+ return -EINVAL;
+ switch (call) {
+ case SEMTIMEDOP:
+ return compat_sys_semtimedop(first, compat_ptr(ptr),
+ second, compat_ptr(third));
+ case SEMOP:
+ /* struct sembuf is the same on 32 and 64bit :)) */
+ return sys_semtimedop(first, compat_ptr(ptr),
+ second, NULL);
+ case SEMGET:
+ return sys_semget(first, second, third);
+ case SEMCTL:
+ return compat_sys_semctl(first, second, third,
+ compat_ptr(ptr));
+ case MSGSND:
+ return compat_sys_msgsnd(first, second, third,
+ compat_ptr(ptr));
+ case MSGRCV:
+ return compat_sys_msgrcv(first, second, 0, third,
+ 0, compat_ptr(ptr));
+ case MSGGET:
+ return sys_msgget((key_t) first, second);
+ case MSGCTL:
+ return compat_sys_msgctl(first, second, compat_ptr(ptr));
+ case SHMAT:
+ return compat_sys_shmat(first, second, third,
+ 0, compat_ptr(ptr));
+ case SHMDT:
+ return sys_shmdt(compat_ptr(ptr));
+ case SHMGET:
+ return sys_shmget(first, (unsigned)second, third);
+ case SHMCTL:
+ return compat_sys_shmctl(first, second, compat_ptr(ptr));
+ }
+
+ return -ENOSYS;
+}
+#endif
+
+asmlinkage long sys32_truncate64(const char __user * path, unsigned long high, unsigned long low)
+{
+ if ((int)high < 0)
+ return -EINVAL;
+ else
+ return sys_truncate(path, (high << 32) | low);
+}
+
+asmlinkage long sys32_ftruncate64(unsigned int fd, unsigned long high, unsigned long low)
+{
+ if ((int)high < 0)
+ return -EINVAL;
+ else
+ return sys_ftruncate(fd, (high << 32) | low);
+}
+
+asmlinkage long sys32_sched_rr_get_interval(compat_pid_t pid,
+ struct compat_timespec __user *interval)
+{
+ struct timespec t;
+ int ret;
+ mm_segment_t old_fs = get_fs ();
+
+ set_fs (KERNEL_DS);
+ ret = sys_sched_rr_get_interval(pid,
+ (struct timespec __force __user *) &t);
+ set_fs (old_fs);
+ if (put_compat_timespec(&t, interval))
+ return -EFAULT;
+ return ret;
+}
+
+asmlinkage long sys32_rt_sigprocmask(int how, compat_sigset_t __user *set,
+ compat_sigset_t __user *oset, size_t sigsetsize)
+{
+ sigset_t s;
+ compat_sigset_t s32;
+ int ret;
+ mm_segment_t old_fs = get_fs();
+
+ if (set) {
+ if (copy_from_user (&s32, set, sizeof(compat_sigset_t)))
+ return -EFAULT;
+ s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32);
+ }
+ set_fs (KERNEL_DS);
+ ret = sys_rt_sigprocmask(how,
+ set ? (sigset_t __force __user *) &s : NULL,
+ oset ? (sigset_t __force __user *) &s : NULL,
+ sigsetsize);
+ set_fs (old_fs);
+ if (ret) return ret;
+ if (oset) {
+ s32.sig[1] = (s.sig[0] >> 32);
+ s32.sig[0] = s.sig[0];
+ if (copy_to_user (oset, &s32, sizeof(compat_sigset_t)))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+asmlinkage long sys32_rt_sigpending(compat_sigset_t __user *set,
+ size_t sigsetsize)
+{
+ sigset_t s;
+ compat_sigset_t s32;
+ int ret;
+ mm_segment_t old_fs = get_fs();
+
+ set_fs (KERNEL_DS);
+ ret = sys_rt_sigpending((sigset_t __force __user *) &s, sigsetsize);
+ set_fs (old_fs);
+ if (!ret) {
+ s32.sig[1] = (s.sig[0] >> 32);
+ s32.sig[0] = s.sig[0];
+ if (copy_to_user (set, &s32, sizeof(compat_sigset_t)))
+ return -EFAULT;
+ }
+ return ret;
+}
+
+asmlinkage long
+sys32_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t __user *uinfo)
+{
+ siginfo_t info;
+ int ret;
+ mm_segment_t old_fs = get_fs();
+
+ if (copy_siginfo_from_user32(&info, uinfo))
+ return -EFAULT;
+ set_fs (KERNEL_DS);
+ ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __force __user *) &info);
+ set_fs (old_fs);
+ return ret;
+}
+
+/*
+ * sys32_execve() executes a new program after the asm stub has set
+ * things up for us. This should basically do what I want it to.
+ */
+asmlinkage long sys32_execve(const char __user *name, compat_uptr_t __user *argv,
+ compat_uptr_t __user *envp)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ char *filename;
+ long rc;
+
+ filename = getname(name);
+ rc = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ return rc;
+ rc = compat_do_execve(filename, argv, envp, regs);
+ if (rc)
+ goto out;
+ current->thread.fp_regs.fpc=0;
+ asm volatile("sfpc %0,0" : : "d" (0));
+ rc = regs->gprs[2];
+out:
+ putname(filename);
+ return rc;
+}
+
+asmlinkage long sys32_pread64(unsigned int fd, char __user *ubuf,
+ size_t count, u32 poshi, u32 poslo)
+{
+ if ((compat_ssize_t) count < 0)
+ return -EINVAL;
+ return sys_pread64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo));
+}
+
+asmlinkage long sys32_pwrite64(unsigned int fd, const char __user *ubuf,
+ size_t count, u32 poshi, u32 poslo)
+{
+ if ((compat_ssize_t) count < 0)
+ return -EINVAL;
+ return sys_pwrite64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo));
+}
+
+asmlinkage compat_ssize_t sys32_readahead(int fd, u32 offhi, u32 offlo, s32 count)
+{
+ return sys_readahead(fd, ((loff_t)AA(offhi) << 32) | AA(offlo), count);
+}
+
+asmlinkage long sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset, size_t count)
+{
+ mm_segment_t old_fs = get_fs();
+ int ret;
+ off_t of;
+
+ if (offset && get_user(of, offset))
+ return -EFAULT;
+
+ set_fs(KERNEL_DS);
+ ret = sys_sendfile(out_fd, in_fd,
+ offset ? (off_t __force __user *) &of : NULL, count);
+ set_fs(old_fs);
+
+ if (offset && put_user(of, offset))
+ return -EFAULT;
+
+ return ret;
+}
+
+asmlinkage long sys32_sendfile64(int out_fd, int in_fd,
+ compat_loff_t __user *offset, s32 count)
+{
+ mm_segment_t old_fs = get_fs();
+ int ret;
+ loff_t lof;
+
+ if (offset && get_user(lof, offset))
+ return -EFAULT;
+
+ set_fs(KERNEL_DS);
+ ret = sys_sendfile64(out_fd, in_fd,
+ offset ? (loff_t __force __user *) &lof : NULL,
+ count);
+ set_fs(old_fs);
+
+ if (offset && put_user(lof, offset))
+ return -EFAULT;
+
+ return ret;
+}
+
+struct stat64_emu31 {
+ unsigned long long st_dev;
+ unsigned int __pad1;
+#define STAT64_HAS_BROKEN_ST_INO 1
+ u32 __st_ino;
+ unsigned int st_mode;
+ unsigned int st_nlink;
+ u32 st_uid;
+ u32 st_gid;
+ unsigned long long st_rdev;
+ unsigned int __pad3;
+ long st_size;
+ u32 st_blksize;
+ unsigned char __pad4[4];
+ u32 __pad5; /* future possible st_blocks high bits */
+ u32 st_blocks; /* Number 512-byte blocks allocated. */
+ u32 st_atime;
+ u32 __pad6;
+ u32 st_mtime;
+ u32 __pad7;
+ u32 st_ctime;
+ u32 __pad8; /* will be high 32 bits of ctime someday */
+ unsigned long st_ino;
+};
+
+static int cp_stat64(struct stat64_emu31 __user *ubuf, struct kstat *stat)
+{
+ struct stat64_emu31 tmp;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ tmp.st_dev = huge_encode_dev(stat->dev);
+ tmp.st_ino = stat->ino;
+ tmp.__st_ino = (u32)stat->ino;
+ tmp.st_mode = stat->mode;
+ tmp.st_nlink = (unsigned int)stat->nlink;
+ tmp.st_uid = stat->uid;
+ tmp.st_gid = stat->gid;
+ tmp.st_rdev = huge_encode_dev(stat->rdev);
+ tmp.st_size = stat->size;
+ tmp.st_blksize = (u32)stat->blksize;
+ tmp.st_blocks = (u32)stat->blocks;
+ tmp.st_atime = (u32)stat->atime.tv_sec;
+ tmp.st_mtime = (u32)stat->mtime.tv_sec;
+ tmp.st_ctime = (u32)stat->ctime.tv_sec;
+
+ return copy_to_user(ubuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
+}
+
+asmlinkage long sys32_stat64(const char __user * filename, struct stat64_emu31 __user * statbuf)
+{
+ struct kstat stat;
+ int ret = vfs_stat(filename, &stat);
+ if (!ret)
+ ret = cp_stat64(statbuf, &stat);
+ return ret;
+}
+
+asmlinkage long sys32_lstat64(const char __user * filename, struct stat64_emu31 __user * statbuf)
+{
+ struct kstat stat;
+ int ret = vfs_lstat(filename, &stat);
+ if (!ret)
+ ret = cp_stat64(statbuf, &stat);
+ return ret;
+}
+
+asmlinkage long sys32_fstat64(unsigned long fd, struct stat64_emu31 __user * statbuf)
+{
+ struct kstat stat;
+ int ret = vfs_fstat(fd, &stat);
+ if (!ret)
+ ret = cp_stat64(statbuf, &stat);
+ return ret;
+}
+
+asmlinkage long sys32_fstatat64(unsigned int dfd, const char __user *filename,
+ struct stat64_emu31 __user* statbuf, int flag)
+{
+ struct kstat stat;
+ int error;
+
+ error = vfs_fstatat(dfd, filename, &stat, flag);
+ if (error)
+ return error;
+ return cp_stat64(statbuf, &stat);
+}
+
+/*
+ * Linux/i386 didn't use to be able to handle more than
+ * 4 system call parameters, so these system calls used a memory
+ * block for parameter passing..
+ */
+
+struct mmap_arg_struct_emu31 {
+ compat_ulong_t addr;
+ compat_ulong_t len;
+ compat_ulong_t prot;
+ compat_ulong_t flags;
+ compat_ulong_t fd;
+ compat_ulong_t offset;
+};
+
+asmlinkage unsigned long old32_mmap(struct mmap_arg_struct_emu31 __user *arg)
+{
+ struct mmap_arg_struct_emu31 a;
+
+ if (copy_from_user(&a, arg, sizeof(a)))
+ return -EFAULT;
+ if (a.offset & ~PAGE_MASK)
+ return -EINVAL;
+ a.addr = (unsigned long) compat_ptr(a.addr);
+ return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
+ a.offset >> PAGE_SHIFT);
+}
+
+asmlinkage long sys32_mmap2(struct mmap_arg_struct_emu31 __user *arg)
+{
+ struct mmap_arg_struct_emu31 a;
+
+ if (copy_from_user(&a, arg, sizeof(a)))
+ return -EFAULT;
+ a.addr = (unsigned long) compat_ptr(a.addr);
+ return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
+}
+
+asmlinkage long sys32_read(unsigned int fd, char __user * buf, size_t count)
+{
+ if ((compat_ssize_t) count < 0)
+ return -EINVAL;
+
+ return sys_read(fd, buf, count);
+}
+
+asmlinkage long sys32_write(unsigned int fd, const char __user * buf, size_t count)
+{
+ if ((compat_ssize_t) count < 0)
+ return -EINVAL;
+
+ return sys_write(fd, buf, count);
+}
+
+/*
+ * 31 bit emulation wrapper functions for sys_fadvise64/fadvise64_64.
+ * These need to rewrite the advise values for POSIX_FADV_{DONTNEED,NOREUSE}
+ * because the 31 bit values differ from the 64 bit values.
+ */
+
+asmlinkage long
+sys32_fadvise64(int fd, loff_t offset, size_t len, int advise)
+{
+ if (advise == 4)
+ advise = POSIX_FADV_DONTNEED;
+ else if (advise == 5)
+ advise = POSIX_FADV_NOREUSE;
+ return sys_fadvise64(fd, offset, len, advise);
+}
+
+struct fadvise64_64_args {
+ int fd;
+ long long offset;
+ long long len;
+ int advice;
+};
+
+asmlinkage long
+sys32_fadvise64_64(struct fadvise64_64_args __user *args)
+{
+ struct fadvise64_64_args a;
+
+ if ( copy_from_user(&a, args, sizeof(a)) )
+ return -EFAULT;
+ if (a.advice == 4)
+ a.advice = POSIX_FADV_DONTNEED;
+ else if (a.advice == 5)
+ a.advice = POSIX_FADV_NOREUSE;
+ return sys_fadvise64_64(a.fd, a.offset, a.len, a.advice);
+}
diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h
new file mode 100644
index 00000000..9635d759
--- /dev/null
+++ b/arch/s390/kernel/compat_linux.h
@@ -0,0 +1,227 @@
+#ifndef _ASM_S390X_S390_H
+#define _ASM_S390X_S390_H
+
+#include <linux/compat.h>
+#include <linux/socket.h>
+#include <linux/syscalls.h>
+
+/* Macro that masks the high order bit of an 32 bit pointer and converts it*/
+/* to a 64 bit pointer */
+#define A(__x) ((unsigned long)((__x) & 0x7FFFFFFFUL))
+#define AA(__x) \
+ ((unsigned long)(__x))
+
+/* Now 32bit compatibility types */
+struct ipc_kludge_32 {
+ __u32 msgp; /* pointer */
+ __s32 msgtyp;
+};
+
+struct old_sigaction32 {
+ __u32 sa_handler; /* Really a pointer, but need to deal with 32 bits */
+ compat_old_sigset_t sa_mask; /* A 32 bit mask */
+ __u32 sa_flags;
+ __u32 sa_restorer; /* Another 32 bit pointer */
+};
+
+typedef struct compat_siginfo {
+ int si_signo;
+ int si_errno;
+ int si_code;
+
+ union {
+ int _pad[((128/sizeof(int)) - 3)];
+
+ /* kill() */
+ struct {
+ pid_t _pid; /* sender's pid */
+ uid_t _uid; /* sender's uid */
+ } _kill;
+
+ /* POSIX.1b timers */
+ struct {
+ compat_timer_t _tid; /* timer id */
+ int _overrun; /* overrun count */
+ compat_sigval_t _sigval; /* same as below */
+ int _sys_private; /* not to be passed to user */
+ } _timer;
+
+ /* POSIX.1b signals */
+ struct {
+ pid_t _pid; /* sender's pid */
+ uid_t _uid; /* sender's uid */
+ compat_sigval_t _sigval;
+ } _rt;
+
+ /* SIGCHLD */
+ struct {
+ pid_t _pid; /* which child */
+ uid_t _uid; /* sender's uid */
+ int _status;/* exit code */
+ compat_clock_t _utime;
+ compat_clock_t _stime;
+ } _sigchld;
+
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+ struct {
+ __u32 _addr; /* faulting insn/memory ref. - pointer */
+ } _sigfault;
+
+ /* SIGPOLL */
+ struct {
+ int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
+ int _fd;
+ } _sigpoll;
+ } _sifields;
+} compat_siginfo_t;
+
+/*
+ * How these fields are to be accessed.
+ */
+#define si_pid _sifields._kill._pid
+#define si_uid _sifields._kill._uid
+#define si_status _sifields._sigchld._status
+#define si_utime _sifields._sigchld._utime
+#define si_stime _sifields._sigchld._stime
+#define si_value _sifields._rt._sigval
+#define si_int _sifields._rt._sigval.sival_int
+#define si_ptr _sifields._rt._sigval.sival_ptr
+#define si_addr _sifields._sigfault._addr
+#define si_band _sifields._sigpoll._band
+#define si_fd _sifields._sigpoll._fd
+#define si_tid _sifields._timer._tid
+#define si_overrun _sifields._timer._overrun
+
+/* asm/sigcontext.h */
+typedef union
+{
+ __u64 d;
+ __u32 f;
+} freg_t32;
+
+typedef struct
+{
+ unsigned int fpc;
+ freg_t32 fprs[__NUM_FPRS];
+} _s390_fp_regs32;
+
+typedef struct
+{
+ __u32 mask;
+ __u32 addr;
+} _psw_t32 __attribute__ ((aligned(8)));
+
+typedef struct
+{
+ _psw_t32 psw;
+ __u32 gprs[__NUM_GPRS];
+ __u32 acrs[__NUM_ACRS];
+} _s390_regs_common32;
+
+typedef struct
+{
+ _s390_regs_common32 regs;
+ _s390_fp_regs32 fpregs;
+} _sigregs32;
+
+#define _SIGCONTEXT_NSIG32 64
+#define _SIGCONTEXT_NSIG_BPW32 32
+#define __SIGNAL_FRAMESIZE32 96
+#define _SIGMASK_COPY_SIZE32 (sizeof(u32)*2)
+
+struct sigcontext32
+{
+ __u32 oldmask[_COMPAT_NSIG_WORDS];
+ __u32 sregs; /* pointer */
+};
+
+/* asm/signal.h */
+struct sigaction32 {
+ __u32 sa_handler; /* pointer */
+ __u32 sa_flags;
+ __u32 sa_restorer; /* pointer */
+ compat_sigset_t sa_mask; /* mask last for extensibility */
+};
+
+typedef struct {
+ __u32 ss_sp; /* pointer */
+ int ss_flags;
+ compat_size_t ss_size;
+} stack_t32;
+
+/* asm/ucontext.h */
+struct ucontext32 {
+ __u32 uc_flags;
+ __u32 uc_link; /* pointer */
+ stack_t32 uc_stack;
+ _sigregs32 uc_mcontext;
+ compat_sigset_t uc_sigmask; /* mask last for extensibility */
+};
+
+struct stat64_emu31;
+struct mmap_arg_struct_emu31;
+struct fadvise64_64_args;
+struct old_sigaction32;
+struct old_sigaction32;
+
+long sys32_chown16(const char __user * filename, u16 user, u16 group);
+long sys32_lchown16(const char __user * filename, u16 user, u16 group);
+long sys32_fchown16(unsigned int fd, u16 user, u16 group);
+long sys32_setregid16(u16 rgid, u16 egid);
+long sys32_setgid16(u16 gid);
+long sys32_setreuid16(u16 ruid, u16 euid);
+long sys32_setuid16(u16 uid);
+long sys32_setresuid16(u16 ruid, u16 euid, u16 suid);
+long sys32_getresuid16(u16 __user *ruid, u16 __user *euid, u16 __user *suid);
+long sys32_setresgid16(u16 rgid, u16 egid, u16 sgid);
+long sys32_getresgid16(u16 __user *rgid, u16 __user *egid, u16 __user *sgid);
+long sys32_setfsuid16(u16 uid);
+long sys32_setfsgid16(u16 gid);
+long sys32_getgroups16(int gidsetsize, u16 __user *grouplist);
+long sys32_setgroups16(int gidsetsize, u16 __user *grouplist);
+long sys32_getuid16(void);
+long sys32_geteuid16(void);
+long sys32_getgid16(void);
+long sys32_getegid16(void);
+long sys32_ipc(u32 call, int first, int second, int third, u32 ptr);
+long sys32_truncate64(const char __user * path, unsigned long high,
+ unsigned long low);
+long sys32_ftruncate64(unsigned int fd, unsigned long high, unsigned long low);
+long sys32_sched_rr_get_interval(compat_pid_t pid,
+ struct compat_timespec __user *interval);
+long sys32_rt_sigprocmask(int how, compat_sigset_t __user *set,
+ compat_sigset_t __user *oset, size_t sigsetsize);
+long sys32_rt_sigpending(compat_sigset_t __user *set, size_t sigsetsize);
+long sys32_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t __user *uinfo);
+long sys32_execve(const char __user *name, compat_uptr_t __user *argv,
+ compat_uptr_t __user *envp);
+long sys32_init_module(void __user *umod, unsigned long len,
+ const char __user *uargs);
+long sys32_delete_module(const char __user *name_user, unsigned int flags);
+long sys32_pread64(unsigned int fd, char __user *ubuf, size_t count,
+ u32 poshi, u32 poslo);
+long sys32_pwrite64(unsigned int fd, const char __user *ubuf,
+ size_t count, u32 poshi, u32 poslo);
+compat_ssize_t sys32_readahead(int fd, u32 offhi, u32 offlo, s32 count);
+long sys32_sendfile(int out_fd, int in_fd, compat_off_t __user *offset,
+ size_t count);
+long sys32_sendfile64(int out_fd, int in_fd, compat_loff_t __user *offset,
+ s32 count);
+long sys32_stat64(const char __user * filename, struct stat64_emu31 __user * statbuf);
+long sys32_lstat64(const char __user * filename,
+ struct stat64_emu31 __user * statbuf);
+long sys32_fstat64(unsigned long fd, struct stat64_emu31 __user * statbuf);
+long sys32_fstatat64(unsigned int dfd, const char __user *filename,
+ struct stat64_emu31 __user* statbuf, int flag);
+unsigned long old32_mmap(struct mmap_arg_struct_emu31 __user *arg);
+long sys32_mmap2(struct mmap_arg_struct_emu31 __user *arg);
+long sys32_read(unsigned int fd, char __user * buf, size_t count);
+long sys32_write(unsigned int fd, const char __user * buf, size_t count);
+long sys32_fadvise64(int fd, loff_t offset, size_t len, int advise);
+long sys32_fadvise64_64(struct fadvise64_64_args __user *args);
+long sys32_sigaction(int sig, const struct old_sigaction32 __user *act,
+ struct old_sigaction32 __user *oact);
+long sys32_rt_sigaction(int sig, const struct sigaction32 __user *act,
+ struct sigaction32 __user *oact, size_t sigsetsize);
+long sys32_sigaltstack(const stack_t32 __user *uss, stack_t32 __user *uoss);
+#endif /* _ASM_S390X_S390_H */
diff --git a/arch/s390/kernel/compat_ptrace.h b/arch/s390/kernel/compat_ptrace.h
new file mode 100644
index 00000000..12b82383
--- /dev/null
+++ b/arch/s390/kernel/compat_ptrace.h
@@ -0,0 +1,63 @@
+#ifndef _PTRACE32_H
+#define _PTRACE32_H
+
+#include <asm/ptrace.h> /* needed for NUM_CR_WORDS */
+#include "compat_linux.h" /* needed for psw_compat_t */
+
+struct compat_per_struct_kernel {
+ __u32 cr9; /* PER control bits */
+ __u32 cr10; /* PER starting address */
+ __u32 cr11; /* PER ending address */
+ __u32 bits; /* Obsolete software bits */
+ __u32 starting_addr; /* User specified start address */
+ __u32 ending_addr; /* User specified end address */
+ __u16 perc_atmid; /* PER trap ATMID */
+ __u32 address; /* PER trap instruction address */
+ __u8 access_id; /* PER trap access identification */
+};
+
+struct compat_user_regs_struct
+{
+ psw_compat_t psw;
+ u32 gprs[NUM_GPRS];
+ u32 acrs[NUM_ACRS];
+ u32 orig_gpr2;
+ /* nb: there's a 4-byte hole here */
+ s390_fp_regs fp_regs;
+ /*
+ * These per registers are in here so that gdb can modify them
+ * itself as there is no "official" ptrace interface for hardware
+ * watchpoints. This is the way intel does it.
+ */
+ struct compat_per_struct_kernel per_info;
+ u32 ieee_instruction_pointer; /* obsolete, always 0 */
+};
+
+struct compat_user {
+ /* We start with the registers, to mimic the way that "memory"
+ is returned from the ptrace(3,...) function. */
+ struct compat_user_regs_struct regs;
+ /* The rest of this junk is to help gdb figure out what goes where */
+ u32 u_tsize; /* Text segment size (pages). */
+ u32 u_dsize; /* Data segment size (pages). */
+ u32 u_ssize; /* Stack segment size (pages). */
+ u32 start_code; /* Starting virtual address of text. */
+ u32 start_stack; /* Starting virtual address of stack area.
+ This is actually the bottom of the stack,
+ the top of the stack is always found in the
+ esp register. */
+ s32 signal; /* Signal that caused the core dump. */
+ u32 u_ar0; /* Used by gdb to help find the values for */
+ /* the registers. */
+ u32 magic; /* To uniquely identify a core file */
+ char u_comm[32]; /* User command that was responsible */
+};
+
+typedef struct
+{
+ __u32 len;
+ __u32 kernel_addr;
+ __u32 process_addr;
+} compat_ptrace_area;
+
+#endif /* _PTRACE32_H */
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c
new file mode 100644
index 00000000..28040fd5
--- /dev/null
+++ b/arch/s390/kernel/compat_signal.c
@@ -0,0 +1,597 @@
+/*
+ * arch/s390/kernel/compat_signal.c
+ *
+ * Copyright (C) IBM Corp. 2000,2006
+ * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
+ * Gerhard Tonn (ton@de.ibm.com)
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
+ */
+
+#include <linux/compat.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/stddef.h>
+#include <linux/tty.h>
+#include <linux/personality.h>
+#include <linux/binfmts.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/lowcore.h>
+#include <asm/switch_to.h>
+#include "compat_linux.h"
+#include "compat_ptrace.h"
+#include "entry.h"
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+typedef struct
+{
+ __u8 callee_used_stack[__SIGNAL_FRAMESIZE32];
+ struct sigcontext32 sc;
+ _sigregs32 sregs;
+ int signo;
+ __u32 gprs_high[NUM_GPRS];
+ __u8 retcode[S390_SYSCALL_SIZE];
+} sigframe32;
+
+typedef struct
+{
+ __u8 callee_used_stack[__SIGNAL_FRAMESIZE32];
+ __u8 retcode[S390_SYSCALL_SIZE];
+ compat_siginfo_t info;
+ struct ucontext32 uc;
+ __u32 gprs_high[NUM_GPRS];
+} rt_sigframe32;
+
+int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
+{
+ int err;
+
+ if (!access_ok (VERIFY_WRITE, to, sizeof(compat_siginfo_t)))
+ return -EFAULT;
+
+ /* If you change siginfo_t structure, please be sure
+ this code is fixed accordingly.
+ It should never copy any pad contained in the structure
+ to avoid security leaks, but must copy the generic
+ 3 ints plus the relevant union member.
+ This routine must convert siginfo from 64bit to 32bit as well
+ at the same time. */
+ err = __put_user(from->si_signo, &to->si_signo);
+ err |= __put_user(from->si_errno, &to->si_errno);
+ err |= __put_user((short)from->si_code, &to->si_code);
+ if (from->si_code < 0)
+ err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
+ else {
+ switch (from->si_code >> 16) {
+ case __SI_RT >> 16: /* This is not generated by the kernel as of now. */
+ case __SI_MESGQ >> 16:
+ err |= __put_user(from->si_int, &to->si_int);
+ /* fallthrough */
+ case __SI_KILL >> 16:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ break;
+ case __SI_CHLD >> 16:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user(from->si_utime, &to->si_utime);
+ err |= __put_user(from->si_stime, &to->si_stime);
+ err |= __put_user(from->si_status, &to->si_status);
+ break;
+ case __SI_FAULT >> 16:
+ err |= __put_user((unsigned long) from->si_addr,
+ &to->si_addr);
+ break;
+ case __SI_POLL >> 16:
+ err |= __put_user(from->si_band, &to->si_band);
+ err |= __put_user(from->si_fd, &to->si_fd);
+ break;
+ case __SI_TIMER >> 16:
+ err |= __put_user(from->si_tid, &to->si_tid);
+ err |= __put_user(from->si_overrun, &to->si_overrun);
+ err |= __put_user(from->si_int, &to->si_int);
+ break;
+ default:
+ break;
+ }
+ }
+ return err;
+}
+
+int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
+{
+ int err;
+ u32 tmp;
+
+ if (!access_ok (VERIFY_READ, from, sizeof(compat_siginfo_t)))
+ return -EFAULT;
+
+ err = __get_user(to->si_signo, &from->si_signo);
+ err |= __get_user(to->si_errno, &from->si_errno);
+ err |= __get_user(to->si_code, &from->si_code);
+
+ if (to->si_code < 0)
+ err |= __copy_from_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE);
+ else {
+ switch (to->si_code >> 16) {
+ case __SI_RT >> 16: /* This is not generated by the kernel as of now. */
+ case __SI_MESGQ >> 16:
+ err |= __get_user(to->si_int, &from->si_int);
+ /* fallthrough */
+ case __SI_KILL >> 16:
+ err |= __get_user(to->si_pid, &from->si_pid);
+ err |= __get_user(to->si_uid, &from->si_uid);
+ break;
+ case __SI_CHLD >> 16:
+ err |= __get_user(to->si_pid, &from->si_pid);
+ err |= __get_user(to->si_uid, &from->si_uid);
+ err |= __get_user(to->si_utime, &from->si_utime);
+ err |= __get_user(to->si_stime, &from->si_stime);
+ err |= __get_user(to->si_status, &from->si_status);
+ break;
+ case __SI_FAULT >> 16:
+ err |= __get_user(tmp, &from->si_addr);
+ to->si_addr = (void __force __user *)
+ (u64) (tmp & PSW32_ADDR_INSN);
+ break;
+ case __SI_POLL >> 16:
+ err |= __get_user(to->si_band, &from->si_band);
+ err |= __get_user(to->si_fd, &from->si_fd);
+ break;
+ case __SI_TIMER >> 16:
+ err |= __get_user(to->si_tid, &from->si_tid);
+ err |= __get_user(to->si_overrun, &from->si_overrun);
+ err |= __get_user(to->si_int, &from->si_int);
+ break;
+ default:
+ break;
+ }
+ }
+ return err;
+}
+
+asmlinkage long
+sys32_sigaction(int sig, const struct old_sigaction32 __user *act,
+ struct old_sigaction32 __user *oact)
+{
+ struct k_sigaction new_ka, old_ka;
+ unsigned long sa_handler, sa_restorer;
+ int ret;
+
+ if (act) {
+ compat_old_sigset_t mask;
+ if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
+ __get_user(sa_handler, &act->sa_handler) ||
+ __get_user(sa_restorer, &act->sa_restorer) ||
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags) ||
+ __get_user(mask, &act->sa_mask))
+ return -EFAULT;
+ new_ka.sa.sa_handler = (__sighandler_t) sa_handler;
+ new_ka.sa.sa_restorer = (void (*)(void)) sa_restorer;
+ siginitset(&new_ka.sa.sa_mask, mask);
+ }
+
+ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+ if (!ret && oact) {
+ sa_handler = (unsigned long) old_ka.sa.sa_handler;
+ sa_restorer = (unsigned long) old_ka.sa.sa_restorer;
+ if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
+ __put_user(sa_handler, &oact->sa_handler) ||
+ __put_user(sa_restorer, &oact->sa_restorer) ||
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags) ||
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask))
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+asmlinkage long
+sys32_rt_sigaction(int sig, const struct sigaction32 __user *act,
+ struct sigaction32 __user *oact, size_t sigsetsize)
+{
+ struct k_sigaction new_ka, old_ka;
+ unsigned long sa_handler;
+ int ret;
+ compat_sigset_t set32;
+
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(compat_sigset_t))
+ return -EINVAL;
+
+ if (act) {
+ ret = get_user(sa_handler, &act->sa_handler);
+ ret |= __copy_from_user(&set32, &act->sa_mask,
+ sizeof(compat_sigset_t));
+ new_ka.sa.sa_mask.sig[0] =
+ set32.sig[0] | (((long)set32.sig[1]) << 32);
+ ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+
+ if (ret)
+ return -EFAULT;
+ new_ka.sa.sa_handler = (__sighandler_t) sa_handler;
+ }
+
+ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+ if (!ret && oact) {
+ set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32);
+ set32.sig[0] = old_ka.sa.sa_mask.sig[0];
+ ret = put_user((unsigned long)old_ka.sa.sa_handler, &oact->sa_handler);
+ ret |= __copy_to_user(&oact->sa_mask, &set32,
+ sizeof(compat_sigset_t));
+ ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+ }
+
+ return ret;
+}
+
+asmlinkage long
+sys32_sigaltstack(const stack_t32 __user *uss, stack_t32 __user *uoss)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ stack_t kss, koss;
+ unsigned long ss_sp;
+ int ret, err = 0;
+ mm_segment_t old_fs = get_fs();
+
+ if (uss) {
+ if (!access_ok(VERIFY_READ, uss, sizeof(*uss)))
+ return -EFAULT;
+ err |= __get_user(ss_sp, &uss->ss_sp);
+ err |= __get_user(kss.ss_size, &uss->ss_size);
+ err |= __get_user(kss.ss_flags, &uss->ss_flags);
+ if (err)
+ return -EFAULT;
+ kss.ss_sp = (void __user *) ss_sp;
+ }
+
+ set_fs (KERNEL_DS);
+ ret = do_sigaltstack((stack_t __force __user *) (uss ? &kss : NULL),
+ (stack_t __force __user *) (uoss ? &koss : NULL),
+ regs->gprs[15]);
+ set_fs (old_fs);
+
+ if (!ret && uoss) {
+ if (!access_ok(VERIFY_WRITE, uoss, sizeof(*uoss)))
+ return -EFAULT;
+ ss_sp = (unsigned long) koss.ss_sp;
+ err |= __put_user(ss_sp, &uoss->ss_sp);
+ err |= __put_user(koss.ss_size, &uoss->ss_size);
+ err |= __put_user(koss.ss_flags, &uoss->ss_flags);
+ if (err)
+ return -EFAULT;
+ }
+ return ret;
+}
+
+static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs)
+{
+ _s390_regs_common32 regs32;
+ int err, i;
+
+ regs32.psw.mask = psw32_user_bits |
+ ((__u32)(regs->psw.mask >> 32) & PSW32_MASK_USER);
+ regs32.psw.addr = (__u32) regs->psw.addr |
+ (__u32)(regs->psw.mask & PSW_MASK_BA);
+ for (i = 0; i < NUM_GPRS; i++)
+ regs32.gprs[i] = (__u32) regs->gprs[i];
+ save_access_regs(current->thread.acrs);
+ memcpy(regs32.acrs, current->thread.acrs, sizeof(regs32.acrs));
+ err = __copy_to_user(&sregs->regs, &regs32, sizeof(regs32));
+ if (err)
+ return err;
+ save_fp_regs(&current->thread.fp_regs);
+ /* s390_fp_regs and _s390_fp_regs32 are the same ! */
+ return __copy_to_user(&sregs->fpregs, &current->thread.fp_regs,
+ sizeof(_s390_fp_regs32));
+}
+
+static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
+{
+ _s390_regs_common32 regs32;
+ int err, i;
+
+ /* Alwys make any pending restarted system call return -EINTR */
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+ err = __copy_from_user(&regs32, &sregs->regs, sizeof(regs32));
+ if (err)
+ return err;
+ regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
+ (__u64)(regs32.psw.mask & PSW32_MASK_USER) << 32 |
+ (__u64)(regs32.psw.addr & PSW32_ADDR_AMODE);
+ regs->psw.addr = (__u64)(regs32.psw.addr & PSW32_ADDR_INSN);
+ for (i = 0; i < NUM_GPRS; i++)
+ regs->gprs[i] = (__u64) regs32.gprs[i];
+ memcpy(current->thread.acrs, regs32.acrs, sizeof(current->thread.acrs));
+ restore_access_regs(current->thread.acrs);
+
+ err = __copy_from_user(&current->thread.fp_regs, &sregs->fpregs,
+ sizeof(_s390_fp_regs32));
+ current->thread.fp_regs.fpc &= FPC_VALID_MASK;
+ if (err)
+ return err;
+
+ restore_fp_regs(&current->thread.fp_regs);
+ clear_thread_flag(TIF_SYSCALL); /* No longer in a system call */
+ return 0;
+}
+
+static int save_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs)
+{
+ __u32 gprs_high[NUM_GPRS];
+ int i;
+
+ for (i = 0; i < NUM_GPRS; i++)
+ gprs_high[i] = regs->gprs[i] >> 32;
+
+ return __copy_to_user(uregs, &gprs_high, sizeof(gprs_high));
+}
+
+static int restore_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs)
+{
+ __u32 gprs_high[NUM_GPRS];
+ int err, i;
+
+ err = __copy_from_user(&gprs_high, uregs, sizeof(gprs_high));
+ if (err)
+ return err;
+ for (i = 0; i < NUM_GPRS; i++)
+ *(__u32 *)&regs->gprs[i] = gprs_high[i];
+ return 0;
+}
+
+asmlinkage long sys32_sigreturn(void)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15];
+ sigset_t set;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32))
+ goto badframe;
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ set_current_blocked(&set);
+ if (restore_sigregs32(regs, &frame->sregs))
+ goto badframe;
+ if (restore_sigregs_gprs_high(regs, frame->gprs_high))
+ goto badframe;
+ return regs->gprs[2];
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage long sys32_rt_sigreturn(void)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15];
+ sigset_t set;
+ stack_t st;
+ __u32 ss_sp;
+ int err;
+ mm_segment_t old_fs = get_fs();
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ set_current_blocked(&set);
+ if (restore_sigregs32(regs, &frame->uc.uc_mcontext))
+ goto badframe;
+ if (restore_sigregs_gprs_high(regs, frame->gprs_high))
+ goto badframe;
+ err = __get_user(ss_sp, &frame->uc.uc_stack.ss_sp);
+ st.ss_sp = compat_ptr(ss_sp);
+ err |= __get_user(st.ss_size, &frame->uc.uc_stack.ss_size);
+ err |= __get_user(st.ss_flags, &frame->uc.uc_stack.ss_flags);
+ if (err)
+ goto badframe;
+ set_fs (KERNEL_DS);
+ do_sigaltstack((stack_t __force __user *)&st, NULL, regs->gprs[15]);
+ set_fs (old_fs);
+ return regs->gprs[2];
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * Set up a signal frame.
+ */
+
+
+/*
+ * Determine which stack to use..
+ */
+static inline void __user *
+get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
+{
+ unsigned long sp;
+
+ /* Default to using normal stack */
+ sp = (unsigned long) A(regs->gprs[15]);
+
+ /* Overflow on alternate signal stack gives SIGSEGV. */
+ if (on_sig_stack(sp) && !on_sig_stack((sp - frame_size) & -8UL))
+ return (void __user *) -1UL;
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa.sa_flags & SA_ONSTACK) {
+ if (! sas_ss_flags(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+ }
+
+ /* This is the legacy signal stack switching. */
+ else if (!user_mode(regs) &&
+ !(ka->sa.sa_flags & SA_RESTORER) &&
+ ka->sa.sa_restorer) {
+ sp = (unsigned long) ka->sa.sa_restorer;
+ }
+
+ return (void __user *)((sp - frame_size) & -8ul);
+}
+
+static inline int map_signal(int sig)
+{
+ if (current_thread_info()->exec_domain
+ && current_thread_info()->exec_domain->signal_invmap
+ && sig < 32)
+ return current_thread_info()->exec_domain->signal_invmap[sig];
+ else
+ return sig;
+}
+
+static int setup_frame32(int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs * regs)
+{
+ sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(sigframe32));
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe32)))
+ goto give_sigsegv;
+
+ if (frame == (void __user *) -1UL)
+ goto give_sigsegv;
+
+ if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE32))
+ goto give_sigsegv;
+
+ if (save_sigregs32(regs, &frame->sregs))
+ goto give_sigsegv;
+ if (save_sigregs_gprs_high(regs, frame->gprs_high))
+ goto give_sigsegv;
+ if (__put_user((unsigned long) &frame->sregs, &frame->sc.sregs))
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ regs->gprs[14] = (__u64) ka->sa.sa_restorer | PSW32_ADDR_AMODE;
+ } else {
+ regs->gprs[14] = (__u64) frame->retcode | PSW32_ADDR_AMODE;
+ if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn,
+ (u16 __force __user *)(frame->retcode)))
+ goto give_sigsegv;
+ }
+
+ /* Set up backchain. */
+ if (__put_user(regs->gprs[15], (unsigned int __user *) frame))
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ regs->gprs[15] = (__force __u64) frame;
+ regs->psw.mask |= PSW_MASK_BA; /* force amode 31 */
+ regs->psw.addr = (__force __u64) ka->sa.sa_handler;
+
+ regs->gprs[2] = map_signal(sig);
+ regs->gprs[3] = (__force __u64) &frame->sc;
+
+ /* We forgot to include these in the sigcontext.
+ To avoid breaking binary compatibility, they are passed as args. */
+ if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
+ sig == SIGTRAP || sig == SIGFPE) {
+ /* set extra registers only for synchronous signals */
+ regs->gprs[4] = regs->int_code & 127;
+ regs->gprs[5] = regs->int_parm_long;
+ }
+
+ /* Place signal number on stack to allow backtrace from handler. */
+ if (__put_user(regs->gprs[2], (int __force __user *) &frame->signo))
+ goto give_sigsegv;
+ return 0;
+
+give_sigsegv:
+ force_sigsegv(sig, current);
+ return -EFAULT;
+}
+
+static int setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs * regs)
+{
+ int err = 0;
+ rt_sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(rt_sigframe32));
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe32)))
+ goto give_sigsegv;
+
+ if (frame == (void __user *) -1UL)
+ goto give_sigsegv;
+
+ if (copy_siginfo_to_user32(&frame->info, info))
+ goto give_sigsegv;
+
+ /* Create the ucontext. */
+ err |= __put_user(UC_EXTENDED, &frame->uc.uc_flags);
+ err |= __put_user(0, &frame->uc.uc_link);
+ err |= __put_user(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(regs->gprs[15]),
+ &frame->uc.uc_stack.ss_flags);
+ err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
+ err |= save_sigregs32(regs, &frame->uc.uc_mcontext);
+ err |= save_sigregs_gprs_high(regs, frame->gprs_high);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ regs->gprs[14] = (__u64) ka->sa.sa_restorer | PSW32_ADDR_AMODE;
+ } else {
+ regs->gprs[14] = (__u64) frame->retcode | PSW32_ADDR_AMODE;
+ err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn,
+ (u16 __force __user *)(frame->retcode));
+ }
+
+ /* Set up backchain. */
+ if (__put_user(regs->gprs[15], (unsigned int __force __user *) frame))
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ regs->gprs[15] = (__force __u64) frame;
+ regs->psw.mask |= PSW_MASK_BA; /* force amode 31 */
+ regs->psw.addr = (__u64) ka->sa.sa_handler;
+
+ regs->gprs[2] = map_signal(sig);
+ regs->gprs[3] = (__force __u64) &frame->info;
+ regs->gprs[4] = (__force __u64) &frame->uc;
+ return 0;
+
+give_sigsegv:
+ force_sigsegv(sig, current);
+ return -EFAULT;
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+
+int handle_signal32(unsigned long sig, struct k_sigaction *ka,
+ siginfo_t *info, sigset_t *oldset, struct pt_regs *regs)
+{
+ int ret;
+
+ /* Set up the stack frame */
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ ret = setup_rt_frame32(sig, ka, info, oldset, regs);
+ else
+ ret = setup_frame32(sig, ka, oldset, regs);
+ if (ret)
+ return ret;
+ block_sigmask(ka, sig);
+ return 0;
+}
+
diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S
new file mode 100644
index 00000000..ff605a39
--- /dev/null
+++ b/arch/s390/kernel/compat_wrapper.S
@@ -0,0 +1,1649 @@
+/*
+* arch/s390/kernel/compat_wrapper.S
+* wrapper for 31 bit compatible system calls.
+*
+* Copyright (C) IBM Corp. 2000,2006
+* Author(s): Gerhard Tonn (ton@de.ibm.com),
+* Thomas Spatzier (tspat@de.ibm.com)
+*/
+
+#include <linux/linkage.h>
+
+ENTRY(sys32_exit_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_exit # branch to sys_exit
+
+ENTRY(sys32_read_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # char *
+ llgfr %r4,%r4 # size_t
+ jg sys32_read # branch to sys_read
+
+ENTRY(sys32_write_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # const char *
+ llgfr %r4,%r4 # size_t
+ jg sys32_write # branch to system call
+
+ENTRY(sys32_open_wrapper)
+ llgtr %r2,%r2 # const char *
+ lgfr %r3,%r3 # int
+ lgfr %r4,%r4 # int
+ jg sys_open # branch to system call
+
+ENTRY(sys32_close_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ jg sys_close # branch to system call
+
+ENTRY(sys32_creat_wrapper)
+ llgtr %r2,%r2 # const char *
+ lgfr %r3,%r3 # int
+ jg sys_creat # branch to system call
+
+ENTRY(sys32_link_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgtr %r3,%r3 # const char *
+ jg sys_link # branch to system call
+
+ENTRY(sys32_unlink_wrapper)
+ llgtr %r2,%r2 # const char *
+ jg sys_unlink # branch to system call
+
+ENTRY(sys32_chdir_wrapper)
+ llgtr %r2,%r2 # const char *
+ jg sys_chdir # branch to system call
+
+ENTRY(sys32_time_wrapper)
+ llgtr %r2,%r2 # int *
+ jg compat_sys_time # branch to system call
+
+ENTRY(sys32_mknod_wrapper)
+ llgtr %r2,%r2 # const char *
+ lgfr %r3,%r3 # int
+ llgfr %r4,%r4 # dev
+ jg sys_mknod # branch to system call
+
+ENTRY(sys32_chmod_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgfr %r3,%r3 # mode_t
+ jg sys_chmod # branch to system call
+
+ENTRY(sys32_lchown16_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgfr %r3,%r3 # __kernel_old_uid_emu31_t
+ llgfr %r4,%r4 # __kernel_old_uid_emu31_t
+ jg sys32_lchown16 # branch to system call
+
+ENTRY(sys32_lseek_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ lgfr %r3,%r3 # off_t
+ llgfr %r4,%r4 # unsigned int
+ jg sys_lseek # branch to system call
+
+#sys32_getpid_wrapper # void
+
+ENTRY(sys32_mount_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # char *
+ llgfr %r5,%r5 # unsigned long
+ llgtr %r6,%r6 # void *
+ jg compat_sys_mount # branch to system call
+
+ENTRY(sys32_oldumount_wrapper)
+ llgtr %r2,%r2 # char *
+ jg sys_oldumount # branch to system call
+
+ENTRY(sys32_setuid16_wrapper)
+ llgfr %r2,%r2 # __kernel_old_uid_emu31_t
+ jg sys32_setuid16 # branch to system call
+
+#sys32_getuid16_wrapper # void
+
+ENTRY(sys32_ptrace_wrapper)
+ lgfr %r2,%r2 # long
+ lgfr %r3,%r3 # long
+ llgtr %r4,%r4 # long
+ llgfr %r5,%r5 # long
+ jg compat_sys_ptrace # branch to system call
+
+ENTRY(sys32_alarm_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ jg sys_alarm # branch to system call
+
+ENTRY(compat_sys_utime_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # struct compat_utimbuf *
+ jg compat_sys_utime # branch to system call
+
+ENTRY(sys32_access_wrapper)
+ llgtr %r2,%r2 # const char *
+ lgfr %r3,%r3 # int
+ jg sys_access # branch to system call
+
+ENTRY(sys32_nice_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_nice # branch to system call
+
+#sys32_sync_wrapper # void
+
+ENTRY(sys32_kill_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ jg sys_kill # branch to system call
+
+ENTRY(sys32_rename_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgtr %r3,%r3 # const char *
+ jg sys_rename # branch to system call
+
+ENTRY(sys32_mkdir_wrapper)
+ llgtr %r2,%r2 # const char *
+ lgfr %r3,%r3 # int
+ jg sys_mkdir # branch to system call
+
+ENTRY(sys32_rmdir_wrapper)
+ llgtr %r2,%r2 # const char *
+ jg sys_rmdir # branch to system call
+
+ENTRY(sys32_dup_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ jg sys_dup # branch to system call
+
+ENTRY(sys32_pipe_wrapper)
+ llgtr %r2,%r2 # u32 *
+ jg sys_pipe # branch to system call
+
+ENTRY(compat_sys_times_wrapper)
+ llgtr %r2,%r2 # struct compat_tms *
+ jg compat_sys_times # branch to system call
+
+ENTRY(sys32_brk_wrapper)
+ llgtr %r2,%r2 # unsigned long
+ jg sys_brk # branch to system call
+
+ENTRY(sys32_setgid16_wrapper)
+ llgfr %r2,%r2 # __kernel_old_gid_emu31_t
+ jg sys32_setgid16 # branch to system call
+
+#sys32_getgid16_wrapper # void
+
+ENTRY(sys32_signal_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # __sighandler_t
+ jg sys_signal
+
+#sys32_geteuid16_wrapper # void
+
+#sys32_getegid16_wrapper # void
+
+ENTRY(sys32_acct_wrapper)
+ llgtr %r2,%r2 # char *
+ jg sys_acct # branch to system call
+
+ENTRY(sys32_umount_wrapper)
+ llgtr %r2,%r2 # char *
+ lgfr %r3,%r3 # int
+ jg sys_umount # branch to system call
+
+ENTRY(compat_sys_ioctl_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # unsigned int
+ llgfr %r4,%r4 # unsigned int
+ jg compat_sys_ioctl # branch to system call
+
+ENTRY(compat_sys_fcntl_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # unsigned int
+ llgfr %r4,%r4 # unsigned long
+ jg compat_sys_fcntl # branch to system call
+
+ENTRY(sys32_setpgid_wrapper)
+ lgfr %r2,%r2 # pid_t
+ lgfr %r3,%r3 # pid_t
+ jg sys_setpgid # branch to system call
+
+ENTRY(sys32_umask_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_umask # branch to system call
+
+ENTRY(sys32_chroot_wrapper)
+ llgtr %r2,%r2 # char *
+ jg sys_chroot # branch to system call
+
+ENTRY(sys32_ustat_wrapper)
+ llgfr %r2,%r2 # dev_t
+ llgtr %r3,%r3 # struct ustat *
+ jg compat_sys_ustat
+
+ENTRY(sys32_dup2_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # unsigned int
+ jg sys_dup2 # branch to system call
+
+#sys32_getppid_wrapper # void
+
+#sys32_getpgrp_wrapper # void
+
+#sys32_setsid_wrapper # void
+
+ENTRY(sys32_sigaction_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const struct old_sigaction *
+ llgtr %r4,%r4 # struct old_sigaction32 *
+ jg sys32_sigaction # branch to system call
+
+ENTRY(sys32_setreuid16_wrapper)
+ llgfr %r2,%r2 # __kernel_old_uid_emu31_t
+ llgfr %r3,%r3 # __kernel_old_uid_emu31_t
+ jg sys32_setreuid16 # branch to system call
+
+ENTRY(sys32_setregid16_wrapper)
+ llgfr %r2,%r2 # __kernel_old_gid_emu31_t
+ llgfr %r3,%r3 # __kernel_old_gid_emu31_t
+ jg sys32_setregid16 # branch to system call
+
+ENTRY(sys_sigsuspend_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ llgfr %r4,%r4 # old_sigset_t
+ jg sys_sigsuspend
+
+ENTRY(compat_sys_sigpending_wrapper)
+ llgtr %r2,%r2 # compat_old_sigset_t *
+ jg compat_sys_sigpending # branch to system call
+
+ENTRY(sys32_sethostname_wrapper)
+ llgtr %r2,%r2 # char *
+ lgfr %r3,%r3 # int
+ jg sys_sethostname # branch to system call
+
+ENTRY(compat_sys_setrlimit_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # struct rlimit_emu31 *
+ jg compat_sys_setrlimit # branch to system call
+
+ENTRY(compat_sys_old_getrlimit_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # struct rlimit_emu31 *
+ jg compat_sys_old_getrlimit # branch to system call
+
+ENTRY(compat_sys_getrlimit_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # struct rlimit_emu31 *
+ jg compat_sys_getrlimit # branch to system call
+
+ENTRY(sys32_mmap2_wrapper)
+ llgtr %r2,%r2 # struct mmap_arg_struct_emu31 *
+ jg sys32_mmap2 # branch to system call
+
+ENTRY(compat_sys_getrusage_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # struct rusage_emu31 *
+ jg compat_sys_getrusage # branch to system call
+
+ENTRY(compat_sys_gettimeofday_wrapper)
+ llgtr %r2,%r2 # struct timeval_emu31 *
+ llgtr %r3,%r3 # struct timezone *
+ jg compat_sys_gettimeofday # branch to system call
+
+ENTRY(compat_sys_settimeofday_wrapper)
+ llgtr %r2,%r2 # struct timeval_emu31 *
+ llgtr %r3,%r3 # struct timezone *
+ jg compat_sys_settimeofday # branch to system call
+
+ENTRY(sys32_getgroups16_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # __kernel_old_gid_emu31_t *
+ jg sys32_getgroups16 # branch to system call
+
+ENTRY(sys32_setgroups16_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # __kernel_old_gid_emu31_t *
+ jg sys32_setgroups16 # branch to system call
+
+ENTRY(sys32_symlink_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgtr %r3,%r3 # const char *
+ jg sys_symlink # branch to system call
+
+ENTRY(sys32_readlink_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgtr %r3,%r3 # char *
+ lgfr %r4,%r4 # int
+ jg sys_readlink # branch to system call
+
+ENTRY(sys32_uselib_wrapper)
+ llgtr %r2,%r2 # const char *
+ jg sys_uselib # branch to system call
+
+ENTRY(sys32_swapon_wrapper)
+ llgtr %r2,%r2 # const char *
+ lgfr %r3,%r3 # int
+ jg sys_swapon # branch to system call
+
+ENTRY(sys32_reboot_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ llgfr %r4,%r4 # unsigned int
+ llgtr %r5,%r5 # void *
+ jg sys_reboot # branch to system call
+
+ENTRY(old32_readdir_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # void *
+ llgfr %r4,%r4 # unsigned int
+ jg compat_sys_old_readdir # branch to system call
+
+ENTRY(old32_mmap_wrapper)
+ llgtr %r2,%r2 # struct mmap_arg_struct_emu31 *
+ jg old32_mmap # branch to system call
+
+ENTRY(sys32_munmap_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgfr %r3,%r3 # size_t
+ jg sys_munmap # branch to system call
+
+ENTRY(sys32_truncate_wrapper)
+ llgtr %r2,%r2 # const char *
+ lgfr %r3,%r3 # long
+ jg sys_truncate # branch to system call
+
+ENTRY(sys32_ftruncate_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # unsigned long
+ jg sys_ftruncate # branch to system call
+
+ENTRY(sys32_fchmod_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # mode_t
+ jg sys_fchmod # branch to system call
+
+ENTRY(sys32_fchown16_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # compat_uid_t
+ llgfr %r4,%r4 # compat_uid_t
+ jg sys32_fchown16 # branch to system call
+
+ENTRY(sys32_getpriority_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ jg sys_getpriority # branch to system call
+
+ENTRY(sys32_setpriority_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ lgfr %r4,%r4 # int
+ jg sys_setpriority # branch to system call
+
+ENTRY(compat_sys_statfs_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # struct compat_statfs *
+ jg compat_sys_statfs # branch to system call
+
+ENTRY(compat_sys_fstatfs_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # struct compat_statfs *
+ jg compat_sys_fstatfs # branch to system call
+
+ENTRY(compat_sys_socketcall_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # u32 *
+ jg compat_sys_socketcall # branch to system call
+
+ENTRY(sys32_syslog_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # char *
+ lgfr %r4,%r4 # int
+ jg sys_syslog # branch to system call
+
+ENTRY(compat_sys_setitimer_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # struct itimerval_emu31 *
+ llgtr %r4,%r4 # struct itimerval_emu31 *
+ jg compat_sys_setitimer # branch to system call
+
+ENTRY(compat_sys_getitimer_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # struct itimerval_emu31 *
+ jg compat_sys_getitimer # branch to system call
+
+ENTRY(compat_sys_newstat_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # struct stat_emu31 *
+ jg compat_sys_newstat # branch to system call
+
+ENTRY(compat_sys_newlstat_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # struct stat_emu31 *
+ jg compat_sys_newlstat # branch to system call
+
+ENTRY(compat_sys_newfstat_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # struct stat_emu31 *
+ jg compat_sys_newfstat # branch to system call
+
+#sys32_vhangup_wrapper # void
+
+ENTRY(compat_sys_wait4_wrapper)
+ lgfr %r2,%r2 # pid_t
+ llgtr %r3,%r3 # unsigned int *
+ lgfr %r4,%r4 # int
+ llgtr %r5,%r5 # struct rusage *
+ jg compat_sys_wait4 # branch to system call
+
+ENTRY(sys32_swapoff_wrapper)
+ llgtr %r2,%r2 # const char *
+ jg sys_swapoff # branch to system call
+
+ENTRY(compat_sys_sysinfo_wrapper)
+ llgtr %r2,%r2 # struct sysinfo_emu31 *
+ jg compat_sys_sysinfo # branch to system call
+
+ENTRY(sys32_ipc_wrapper)
+ llgfr %r2,%r2 # uint
+ lgfr %r3,%r3 # int
+ lgfr %r4,%r4 # int
+ lgfr %r5,%r5 # int
+ llgfr %r6,%r6 # u32
+ jg sys32_ipc # branch to system call
+
+ENTRY(sys32_fsync_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ jg sys_fsync # branch to system call
+
+#sys32_sigreturn_wrapper # done in sigreturn_glue
+
+#sys32_clone_wrapper # done in clone_glue
+
+ENTRY(sys32_setdomainname_wrapper)
+ llgtr %r2,%r2 # char *
+ lgfr %r3,%r3 # int
+ jg sys_setdomainname # branch to system call
+
+ENTRY(sys32_newuname_wrapper)
+ llgtr %r2,%r2 # struct new_utsname *
+ jg sys_newuname # branch to system call
+
+ENTRY(compat_sys_adjtimex_wrapper)
+ llgtr %r2,%r2 # struct compat_timex *
+ jg compat_sys_adjtimex # branch to system call
+
+ENTRY(sys32_mprotect_wrapper)
+ llgtr %r2,%r2 # unsigned long (actually pointer
+ llgfr %r3,%r3 # size_t
+ llgfr %r4,%r4 # unsigned long
+ jg sys_mprotect # branch to system call
+
+ENTRY(compat_sys_sigprocmask_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # compat_old_sigset_t *
+ llgtr %r4,%r4 # compat_old_sigset_t *
+ jg compat_sys_sigprocmask # branch to system call
+
+ENTRY(sys_init_module_wrapper)
+ llgtr %r2,%r2 # void *
+ llgfr %r3,%r3 # unsigned long
+ llgtr %r4,%r4 # char *
+ jg sys_init_module # branch to system call
+
+ENTRY(sys_delete_module_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgfr %r3,%r3 # unsigned int
+ jg sys_delete_module # branch to system call
+
+ENTRY(sys32_quotactl_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # const char *
+ llgfr %r4,%r4 # qid_t
+ llgtr %r5,%r5 # caddr_t
+ jg sys_quotactl # branch to system call
+
+ENTRY(sys32_getpgid_wrapper)
+ lgfr %r2,%r2 # pid_t
+ jg sys_getpgid # branch to system call
+
+ENTRY(sys32_fchdir_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ jg sys_fchdir # branch to system call
+
+ENTRY(sys32_bdflush_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # long
+ jg sys_bdflush # branch to system call
+
+ENTRY(sys32_sysfs_wrapper)
+ lgfr %r2,%r2 # int
+ llgfr %r3,%r3 # unsigned long
+ llgfr %r4,%r4 # unsigned long
+ jg sys_sysfs # branch to system call
+
+ENTRY(sys32_personality_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ jg sys_s390_personality # branch to system call
+
+ENTRY(sys32_setfsuid16_wrapper)
+ llgfr %r2,%r2 # __kernel_old_uid_emu31_t
+ jg sys32_setfsuid16 # branch to system call
+
+ENTRY(sys32_setfsgid16_wrapper)
+ llgfr %r2,%r2 # __kernel_old_gid_emu31_t
+ jg sys32_setfsgid16 # branch to system call
+
+ENTRY(sys32_llseek_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # unsigned long
+ llgfr %r4,%r4 # unsigned long
+ llgtr %r5,%r5 # loff_t *
+ llgfr %r6,%r6 # unsigned int
+ jg sys_llseek # branch to system call
+
+ENTRY(sys32_getdents_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # void *
+ llgfr %r4,%r4 # unsigned int
+ jg compat_sys_getdents # branch to system call
+
+ENTRY(compat_sys_select_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # compat_fd_set *
+ llgtr %r4,%r4 # compat_fd_set *
+ llgtr %r5,%r5 # compat_fd_set *
+ llgtr %r6,%r6 # struct compat_timeval *
+ jg compat_sys_select # branch to system call
+
+ENTRY(sys32_flock_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # unsigned int
+ jg sys_flock # branch to system call
+
+ENTRY(sys32_msync_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgfr %r3,%r3 # size_t
+ lgfr %r4,%r4 # int
+ jg sys_msync # branch to system call
+
+ENTRY(compat_sys_readv_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const struct compat_iovec *
+ llgfr %r4,%r4 # unsigned long
+ jg compat_sys_readv # branch to system call
+
+ENTRY(compat_sys_writev_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const struct compat_iovec *
+ llgfr %r4,%r4 # unsigned long
+ jg compat_sys_writev # branch to system call
+
+ENTRY(sys32_getsid_wrapper)
+ lgfr %r2,%r2 # pid_t
+ jg sys_getsid # branch to system call
+
+ENTRY(sys32_fdatasync_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ jg sys_fdatasync # branch to system call
+
+ENTRY(sys32_mlock_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgfr %r3,%r3 # size_t
+ jg sys_mlock # branch to system call
+
+ENTRY(sys32_munlock_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgfr %r3,%r3 # size_t
+ jg sys_munlock # branch to system call
+
+ENTRY(sys32_mlockall_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_mlockall # branch to system call
+
+#sys32_munlockall_wrapper # void
+
+ENTRY(sys32_sched_setparam_wrapper)
+ lgfr %r2,%r2 # pid_t
+ llgtr %r3,%r3 # struct sched_param *
+ jg sys_sched_setparam # branch to system call
+
+ENTRY(sys32_sched_getparam_wrapper)
+ lgfr %r2,%r2 # pid_t
+ llgtr %r3,%r3 # struct sched_param *
+ jg sys_sched_getparam # branch to system call
+
+ENTRY(sys32_sched_setscheduler_wrapper)
+ lgfr %r2,%r2 # pid_t
+ lgfr %r3,%r3 # int
+ llgtr %r4,%r4 # struct sched_param *
+ jg sys_sched_setscheduler # branch to system call
+
+ENTRY(sys32_sched_getscheduler_wrapper)
+ lgfr %r2,%r2 # pid_t
+ jg sys_sched_getscheduler # branch to system call
+
+#sys32_sched_yield_wrapper # void
+
+ENTRY(sys32_sched_get_priority_max_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_sched_get_priority_max # branch to system call
+
+ENTRY(sys32_sched_get_priority_min_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_sched_get_priority_min # branch to system call
+
+ENTRY(sys32_sched_rr_get_interval_wrapper)
+ lgfr %r2,%r2 # pid_t
+ llgtr %r3,%r3 # struct compat_timespec *
+ jg sys32_sched_rr_get_interval # branch to system call
+
+ENTRY(compat_sys_nanosleep_wrapper)
+ llgtr %r2,%r2 # struct compat_timespec *
+ llgtr %r3,%r3 # struct compat_timespec *
+ jg compat_sys_nanosleep # branch to system call
+
+ENTRY(sys32_mremap_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgfr %r3,%r3 # unsigned long
+ llgfr %r4,%r4 # unsigned long
+ llgfr %r5,%r5 # unsigned long
+ llgfr %r6,%r6 # unsigned long
+ jg sys_mremap # branch to system call
+
+ENTRY(sys32_setresuid16_wrapper)
+ llgfr %r2,%r2 # __kernel_old_uid_emu31_t
+ llgfr %r3,%r3 # __kernel_old_uid_emu31_t
+ llgfr %r4,%r4 # __kernel_old_uid_emu31_t
+ jg sys32_setresuid16 # branch to system call
+
+ENTRY(sys32_getresuid16_wrapper)
+ llgtr %r2,%r2 # __kernel_old_uid_emu31_t *
+ llgtr %r3,%r3 # __kernel_old_uid_emu31_t *
+ llgtr %r4,%r4 # __kernel_old_uid_emu31_t *
+ jg sys32_getresuid16 # branch to system call
+
+ENTRY(sys32_poll_wrapper)
+ llgtr %r2,%r2 # struct pollfd *
+ llgfr %r3,%r3 # unsigned int
+ lgfr %r4,%r4 # int
+ jg sys_poll # branch to system call
+
+ENTRY(sys32_setresgid16_wrapper)
+ llgfr %r2,%r2 # __kernel_old_gid_emu31_t
+ llgfr %r3,%r3 # __kernel_old_gid_emu31_t
+ llgfr %r4,%r4 # __kernel_old_gid_emu31_t
+ jg sys32_setresgid16 # branch to system call
+
+ENTRY(sys32_getresgid16_wrapper)
+ llgtr %r2,%r2 # __kernel_old_gid_emu31_t *
+ llgtr %r3,%r3 # __kernel_old_gid_emu31_t *
+ llgtr %r4,%r4 # __kernel_old_gid_emu31_t *
+ jg sys32_getresgid16 # branch to system call
+
+ENTRY(sys32_prctl_wrapper)
+ lgfr %r2,%r2 # int
+ llgfr %r3,%r3 # unsigned long
+ llgfr %r4,%r4 # unsigned long
+ llgfr %r5,%r5 # unsigned long
+ llgfr %r6,%r6 # unsigned long
+ jg sys_prctl # branch to system call
+
+#sys32_rt_sigreturn_wrapper # done in rt_sigreturn_glue
+
+ENTRY(sys32_rt_sigaction_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const struct sigaction_emu31 *
+ llgtr %r4,%r4 # const struct sigaction_emu31 *
+ llgfr %r5,%r5 # size_t
+ jg sys32_rt_sigaction # branch to system call
+
+ENTRY(sys32_rt_sigprocmask_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # old_sigset_emu31 *
+ llgtr %r4,%r4 # old_sigset_emu31 *
+ llgfr %r5,%r5 # size_t
+ jg sys32_rt_sigprocmask # branch to system call
+
+ENTRY(sys32_rt_sigpending_wrapper)
+ llgtr %r2,%r2 # sigset_emu31 *
+ llgfr %r3,%r3 # size_t
+ jg sys32_rt_sigpending # branch to system call
+
+ENTRY(compat_sys_rt_sigtimedwait_wrapper)
+ llgtr %r2,%r2 # const sigset_emu31_t *
+ llgtr %r3,%r3 # siginfo_emu31_t *
+ llgtr %r4,%r4 # const struct compat_timespec *
+ llgfr %r5,%r5 # size_t
+ jg compat_sys_rt_sigtimedwait # branch to system call
+
+ENTRY(sys32_rt_sigqueueinfo_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ llgtr %r4,%r4 # siginfo_emu31_t *
+ jg sys32_rt_sigqueueinfo # branch to system call
+
+ENTRY(compat_sys_rt_sigsuspend_wrapper)
+ llgtr %r2,%r2 # compat_sigset_t *
+ llgfr %r3,%r3 # compat_size_t
+ jg compat_sys_rt_sigsuspend
+
+ENTRY(sys32_pread64_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # char *
+ llgfr %r4,%r4 # size_t
+ llgfr %r5,%r5 # u32
+ llgfr %r6,%r6 # u32
+ jg sys32_pread64 # branch to system call
+
+ENTRY(sys32_pwrite64_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # const char *
+ llgfr %r4,%r4 # size_t
+ llgfr %r5,%r5 # u32
+ llgfr %r6,%r6 # u32
+ jg sys32_pwrite64 # branch to system call
+
+ENTRY(sys32_chown16_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgfr %r3,%r3 # __kernel_old_uid_emu31_t
+ llgfr %r4,%r4 # __kernel_old_gid_emu31_t
+ jg sys32_chown16 # branch to system call
+
+ENTRY(sys32_getcwd_wrapper)
+ llgtr %r2,%r2 # char *
+ llgfr %r3,%r3 # unsigned long
+ jg sys_getcwd # branch to system call
+
+ENTRY(sys32_capget_wrapper)
+ llgtr %r2,%r2 # cap_user_header_t
+ llgtr %r3,%r3 # cap_user_data_t
+ jg sys_capget # branch to system call
+
+ENTRY(sys32_capset_wrapper)
+ llgtr %r2,%r2 # cap_user_header_t
+ llgtr %r3,%r3 # const cap_user_data_t
+ jg sys_capset # branch to system call
+
+ENTRY(sys32_sigaltstack_wrapper)
+ llgtr %r2,%r2 # const stack_emu31_t *
+ llgtr %r3,%r3 # stack_emu31_t *
+ jg sys32_sigaltstack
+
+ENTRY(sys32_sendfile_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ llgtr %r4,%r4 # __kernel_off_emu31_t *
+ llgfr %r5,%r5 # size_t
+ jg sys32_sendfile # branch to system call
+
+#sys32_vfork_wrapper # done in vfork_glue
+
+ENTRY(sys32_truncate64_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgfr %r3,%r3 # unsigned long
+ llgfr %r4,%r4 # unsigned long
+ jg sys32_truncate64 # branch to system call
+
+ENTRY(sys32_ftruncate64_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # unsigned long
+ llgfr %r4,%r4 # unsigned long
+ jg sys32_ftruncate64 # branch to system call
+
+ENTRY(sys32_lchown_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgfr %r3,%r3 # uid_t
+ llgfr %r4,%r4 # gid_t
+ jg sys_lchown # branch to system call
+
+#sys32_getuid_wrapper # void
+#sys32_getgid_wrapper # void
+#sys32_geteuid_wrapper # void
+#sys32_getegid_wrapper # void
+
+ENTRY(sys32_setreuid_wrapper)
+ llgfr %r2,%r2 # uid_t
+ llgfr %r3,%r3 # uid_t
+ jg sys_setreuid # branch to system call
+
+ENTRY(sys32_setregid_wrapper)
+ llgfr %r2,%r2 # gid_t
+ llgfr %r3,%r3 # gid_t
+ jg sys_setregid # branch to system call
+
+ENTRY(sys32_getgroups_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # gid_t *
+ jg sys_getgroups # branch to system call
+
+ENTRY(sys32_setgroups_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # gid_t *
+ jg sys_setgroups # branch to system call
+
+ENTRY(sys32_fchown_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # uid_t
+ llgfr %r4,%r4 # gid_t
+ jg sys_fchown # branch to system call
+
+ENTRY(sys32_setresuid_wrapper)
+ llgfr %r2,%r2 # uid_t
+ llgfr %r3,%r3 # uid_t
+ llgfr %r4,%r4 # uid_t
+ jg sys_setresuid # branch to system call
+
+ENTRY(sys32_getresuid_wrapper)
+ llgtr %r2,%r2 # uid_t *
+ llgtr %r3,%r3 # uid_t *
+ llgtr %r4,%r4 # uid_t *
+ jg sys_getresuid # branch to system call
+
+ENTRY(sys32_setresgid_wrapper)
+ llgfr %r2,%r2 # gid_t
+ llgfr %r3,%r3 # gid_t
+ llgfr %r4,%r4 # gid_t
+ jg sys_setresgid # branch to system call
+
+ENTRY(sys32_getresgid_wrapper)
+ llgtr %r2,%r2 # gid_t *
+ llgtr %r3,%r3 # gid_t *
+ llgtr %r4,%r4 # gid_t *
+ jg sys_getresgid # branch to system call
+
+ENTRY(sys32_chown_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgfr %r3,%r3 # uid_t
+ llgfr %r4,%r4 # gid_t
+ jg sys_chown # branch to system call
+
+ENTRY(sys32_setuid_wrapper)
+ llgfr %r2,%r2 # uid_t
+ jg sys_setuid # branch to system call
+
+ENTRY(sys32_setgid_wrapper)
+ llgfr %r2,%r2 # gid_t
+ jg sys_setgid # branch to system call
+
+ENTRY(sys32_setfsuid_wrapper)
+ llgfr %r2,%r2 # uid_t
+ jg sys_setfsuid # branch to system call
+
+ENTRY(sys32_setfsgid_wrapper)
+ llgfr %r2,%r2 # gid_t
+ jg sys_setfsgid # branch to system call
+
+ENTRY(sys32_pivot_root_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgtr %r3,%r3 # const char *
+ jg sys_pivot_root # branch to system call
+
+ENTRY(sys32_mincore_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgfr %r3,%r3 # size_t
+ llgtr %r4,%r4 # unsigned char *
+ jg sys_mincore # branch to system call
+
+ENTRY(sys32_madvise_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgfr %r3,%r3 # size_t
+ lgfr %r4,%r4 # int
+ jg sys_madvise # branch to system call
+
+ENTRY(sys32_getdents64_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # void *
+ llgfr %r4,%r4 # unsigned int
+ jg sys_getdents64 # branch to system call
+
+ENTRY(compat_sys_fcntl64_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # unsigned int
+ llgfr %r4,%r4 # unsigned long
+ jg compat_sys_fcntl64 # branch to system call
+
+ENTRY(sys32_stat64_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # struct stat64 *
+ jg sys32_stat64 # branch to system call
+
+ENTRY(sys32_lstat64_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # struct stat64 *
+ jg sys32_lstat64 # branch to system call
+
+ENTRY(sys32_stime_wrapper)
+ llgtr %r2,%r2 # long *
+ jg compat_sys_stime # branch to system call
+
+ENTRY(sys32_sysctl_wrapper)
+ llgtr %r2,%r2 # struct compat_sysctl_args *
+ jg compat_sys_sysctl
+
+ENTRY(sys32_fstat64_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgtr %r3,%r3 # struct stat64 *
+ jg sys32_fstat64 # branch to system call
+
+ENTRY(compat_sys_futex_wrapper)
+ llgtr %r2,%r2 # u32 *
+ lgfr %r3,%r3 # int
+ lgfr %r4,%r4 # int
+ llgtr %r5,%r5 # struct compat_timespec *
+ llgtr %r6,%r6 # u32 *
+ lgf %r0,164(%r15) # int
+ stg %r0,160(%r15)
+ jg compat_sys_futex # branch to system call
+
+ENTRY(sys32_setxattr_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ lgfr %r6,%r6 # int
+ jg sys_setxattr
+
+ENTRY(sys32_lsetxattr_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ lgfr %r6,%r6 # int
+ jg sys_lsetxattr
+
+ENTRY(sys32_fsetxattr_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ lgfr %r6,%r6 # int
+ jg sys_fsetxattr
+
+ENTRY(sys32_getxattr_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ jg sys_getxattr
+
+ENTRY(sys32_lgetxattr_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ jg sys_lgetxattr
+
+ENTRY(sys32_fgetxattr_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # void *
+ llgfr %r5,%r5 # size_t
+ jg sys_fgetxattr
+
+ENTRY(sys32_listxattr_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgfr %r4,%r4 # size_t
+ jg sys_listxattr
+
+ENTRY(sys32_llistxattr_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ llgfr %r4,%r4 # size_t
+ jg sys_llistxattr
+
+ENTRY(sys32_flistxattr_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # char *
+ llgfr %r4,%r4 # size_t
+ jg sys_flistxattr
+
+ENTRY(sys32_removexattr_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ jg sys_removexattr
+
+ENTRY(sys32_lremovexattr_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # char *
+ jg sys_lremovexattr
+
+ENTRY(sys32_fremovexattr_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # char *
+ jg sys_fremovexattr
+
+ENTRY(sys32_sched_setaffinity_wrapper)
+ lgfr %r2,%r2 # int
+ llgfr %r3,%r3 # unsigned int
+ llgtr %r4,%r4 # unsigned long *
+ jg compat_sys_sched_setaffinity
+
+ENTRY(sys32_sched_getaffinity_wrapper)
+ lgfr %r2,%r2 # int
+ llgfr %r3,%r3 # unsigned int
+ llgtr %r4,%r4 # unsigned long *
+ jg compat_sys_sched_getaffinity
+
+ENTRY(sys32_exit_group_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_exit_group # branch to system call
+
+ENTRY(sys32_set_tid_address_wrapper)
+ llgtr %r2,%r2 # int *
+ jg sys_set_tid_address # branch to system call
+
+ENTRY(sys_epoll_create_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_epoll_create # branch to system call
+
+ENTRY(sys_epoll_ctl_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ lgfr %r4,%r4 # int
+ llgtr %r5,%r5 # struct epoll_event *
+ jg sys_epoll_ctl # branch to system call
+
+ENTRY(sys_epoll_wait_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # struct epoll_event *
+ lgfr %r4,%r4 # int
+ lgfr %r5,%r5 # int
+ jg sys_epoll_wait # branch to system call
+
+ENTRY(sys32_lookup_dcookie_wrapper)
+ sllg %r2,%r2,32 # get high word of 64bit dcookie
+ or %r2,%r3 # get low word of 64bit dcookie
+ llgtr %r3,%r4 # char *
+ llgfr %r4,%r5 # size_t
+ jg sys_lookup_dcookie
+
+ENTRY(sys32_fadvise64_wrapper)
+ lgfr %r2,%r2 # int
+ sllg %r3,%r3,32 # get high word of 64bit loff_t
+ or %r3,%r4 # get low word of 64bit loff_t
+ llgfr %r4,%r5 # size_t (unsigned long)
+ lgfr %r5,%r6 # int
+ jg sys32_fadvise64
+
+ENTRY(sys32_fadvise64_64_wrapper)
+ llgtr %r2,%r2 # struct fadvise64_64_args *
+ jg sys32_fadvise64_64
+
+ENTRY(sys32_clock_settime_wrapper)
+ lgfr %r2,%r2 # clockid_t (int)
+ llgtr %r3,%r3 # struct compat_timespec *
+ jg compat_sys_clock_settime
+
+ENTRY(sys32_clock_gettime_wrapper)
+ lgfr %r2,%r2 # clockid_t (int)
+ llgtr %r3,%r3 # struct compat_timespec *
+ jg compat_sys_clock_gettime
+
+ENTRY(sys32_clock_getres_wrapper)
+ lgfr %r2,%r2 # clockid_t (int)
+ llgtr %r3,%r3 # struct compat_timespec *
+ jg compat_sys_clock_getres
+
+ENTRY(sys32_clock_nanosleep_wrapper)
+ lgfr %r2,%r2 # clockid_t (int)
+ lgfr %r3,%r3 # int
+ llgtr %r4,%r4 # struct compat_timespec *
+ llgtr %r5,%r5 # struct compat_timespec *
+ jg compat_sys_clock_nanosleep
+
+ENTRY(sys32_timer_create_wrapper)
+ lgfr %r2,%r2 # timer_t (int)
+ llgtr %r3,%r3 # struct compat_sigevent *
+ llgtr %r4,%r4 # timer_t *
+ jg compat_sys_timer_create
+
+ENTRY(sys32_timer_settime_wrapper)
+ lgfr %r2,%r2 # timer_t (int)
+ lgfr %r3,%r3 # int
+ llgtr %r4,%r4 # struct compat_itimerspec *
+ llgtr %r5,%r5 # struct compat_itimerspec *
+ jg compat_sys_timer_settime
+
+ENTRY(sys32_timer_gettime_wrapper)
+ lgfr %r2,%r2 # timer_t (int)
+ llgtr %r3,%r3 # struct compat_itimerspec *
+ jg compat_sys_timer_gettime
+
+ENTRY(sys32_timer_getoverrun_wrapper)
+ lgfr %r2,%r2 # timer_t (int)
+ jg sys_timer_getoverrun
+
+ENTRY(sys32_timer_delete_wrapper)
+ lgfr %r2,%r2 # timer_t (int)
+ jg sys_timer_delete
+
+ENTRY(sys32_io_setup_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # u32 *
+ jg compat_sys_io_setup
+
+ENTRY(sys32_io_destroy_wrapper)
+ llgfr %r2,%r2 # (aio_context_t) u32
+ jg sys_io_destroy
+
+ENTRY(sys32_io_getevents_wrapper)
+ llgfr %r2,%r2 # (aio_context_t) u32
+ lgfr %r3,%r3 # long
+ lgfr %r4,%r4 # long
+ llgtr %r5,%r5 # struct io_event *
+ llgtr %r6,%r6 # struct compat_timespec *
+ jg compat_sys_io_getevents
+
+ENTRY(sys32_io_submit_wrapper)
+ llgfr %r2,%r2 # (aio_context_t) u32
+ lgfr %r3,%r3 # long
+ llgtr %r4,%r4 # struct iocb **
+ jg compat_sys_io_submit
+
+ENTRY(sys32_io_cancel_wrapper)
+ llgfr %r2,%r2 # (aio_context_t) u32
+ llgtr %r3,%r3 # struct iocb *
+ llgtr %r4,%r4 # struct io_event *
+ jg sys_io_cancel
+
+ENTRY(compat_sys_statfs64_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgfr %r3,%r3 # compat_size_t
+ llgtr %r4,%r4 # struct compat_statfs64 *
+ jg compat_sys_statfs64
+
+ENTRY(compat_sys_fstatfs64_wrapper)
+ llgfr %r2,%r2 # unsigned int fd
+ llgfr %r3,%r3 # compat_size_t
+ llgtr %r4,%r4 # struct compat_statfs64 *
+ jg compat_sys_fstatfs64
+
+ENTRY(compat_sys_mq_open_wrapper)
+ llgtr %r2,%r2 # const char *
+ lgfr %r3,%r3 # int
+ llgfr %r4,%r4 # mode_t
+ llgtr %r5,%r5 # struct compat_mq_attr *
+ jg compat_sys_mq_open
+
+ENTRY(sys32_mq_unlink_wrapper)
+ llgtr %r2,%r2 # const char *
+ jg sys_mq_unlink
+
+ENTRY(compat_sys_mq_timedsend_wrapper)
+ lgfr %r2,%r2 # mqd_t
+ llgtr %r3,%r3 # const char *
+ llgfr %r4,%r4 # size_t
+ llgfr %r5,%r5 # unsigned int
+ llgtr %r6,%r6 # const struct compat_timespec *
+ jg compat_sys_mq_timedsend
+
+ENTRY(compat_sys_mq_timedreceive_wrapper)
+ lgfr %r2,%r2 # mqd_t
+ llgtr %r3,%r3 # char *
+ llgfr %r4,%r4 # size_t
+ llgtr %r5,%r5 # unsigned int *
+ llgtr %r6,%r6 # const struct compat_timespec *
+ jg compat_sys_mq_timedreceive
+
+ENTRY(compat_sys_mq_notify_wrapper)
+ lgfr %r2,%r2 # mqd_t
+ llgtr %r3,%r3 # struct compat_sigevent *
+ jg compat_sys_mq_notify
+
+ENTRY(compat_sys_mq_getsetattr_wrapper)
+ lgfr %r2,%r2 # mqd_t
+ llgtr %r3,%r3 # struct compat_mq_attr *
+ llgtr %r4,%r4 # struct compat_mq_attr *
+ jg compat_sys_mq_getsetattr
+
+ENTRY(compat_sys_add_key_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgtr %r3,%r3 # const char *
+ llgtr %r4,%r4 # const void *
+ llgfr %r5,%r5 # size_t
+ llgfr %r6,%r6 # (key_serial_t) u32
+ jg sys_add_key
+
+ENTRY(compat_sys_request_key_wrapper)
+ llgtr %r2,%r2 # const char *
+ llgtr %r3,%r3 # const char *
+ llgtr %r4,%r4 # const void *
+ llgfr %r5,%r5 # (key_serial_t) u32
+ jg sys_request_key
+
+ENTRY(sys32_remap_file_pages_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgfr %r3,%r3 # unsigned long
+ llgfr %r4,%r4 # unsigned long
+ llgfr %r5,%r5 # unsigned long
+ llgfr %r6,%r6 # unsigned long
+ jg sys_remap_file_pages
+
+ENTRY(compat_sys_waitid_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # pid_t
+ llgtr %r4,%r4 # siginfo_emu31_t *
+ lgfr %r5,%r5 # int
+ llgtr %r6,%r6 # struct rusage_emu31 *
+ jg compat_sys_waitid
+
+ENTRY(compat_sys_kexec_load_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgfr %r3,%r3 # unsigned long
+ llgtr %r4,%r4 # struct kexec_segment *
+ llgfr %r5,%r5 # unsigned long
+ jg compat_sys_kexec_load
+
+ENTRY(sys_ioprio_set_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ lgfr %r4,%r4 # int
+ jg sys_ioprio_set
+
+ENTRY(sys_ioprio_get_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ jg sys_ioprio_get
+
+ENTRY(sys_inotify_add_watch_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char *
+ llgfr %r4,%r4 # u32
+ jg sys_inotify_add_watch
+
+ENTRY(sys_inotify_rm_watch_wrapper)
+ lgfr %r2,%r2 # int
+ llgfr %r3,%r3 # u32
+ jg sys_inotify_rm_watch
+
+ENTRY(compat_sys_openat_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # const char *
+ lgfr %r4,%r4 # int
+ lgfr %r5,%r5 # int
+ jg compat_sys_openat
+
+ENTRY(sys_mkdirat_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char *
+ lgfr %r4,%r4 # int
+ jg sys_mkdirat
+
+ENTRY(sys_mknodat_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char *
+ lgfr %r4,%r4 # int
+ llgfr %r5,%r5 # unsigned int
+ jg sys_mknodat
+
+ENTRY(sys_fchownat_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char *
+ llgfr %r4,%r4 # uid_t
+ llgfr %r5,%r5 # gid_t
+ lgfr %r6,%r6 # int
+ jg sys_fchownat
+
+ENTRY(compat_sys_futimesat_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # struct timeval *
+ jg compat_sys_futimesat
+
+ENTRY(sys32_fstatat64_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # struct stat64 *
+ lgfr %r5,%r5 # int
+ jg sys32_fstatat64
+
+ENTRY(sys_unlinkat_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char *
+ lgfr %r4,%r4 # int
+ jg sys_unlinkat
+
+ENTRY(sys_renameat_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char *
+ lgfr %r4,%r4 # int
+ llgtr %r5,%r5 # const char *
+ jg sys_renameat
+
+ENTRY(sys_linkat_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char *
+ lgfr %r4,%r4 # int
+ llgtr %r5,%r5 # const char *
+ lgfr %r6,%r6 # int
+ jg sys_linkat
+
+ENTRY(sys_symlinkat_wrapper)
+ llgtr %r2,%r2 # const char *
+ lgfr %r3,%r3 # int
+ llgtr %r4,%r4 # const char *
+ jg sys_symlinkat
+
+ENTRY(sys_readlinkat_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char *
+ llgtr %r4,%r4 # char *
+ lgfr %r5,%r5 # int
+ jg sys_readlinkat
+
+ENTRY(sys_fchmodat_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char *
+ llgfr %r4,%r4 # mode_t
+ jg sys_fchmodat
+
+ENTRY(sys_faccessat_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char *
+ lgfr %r4,%r4 # int
+ jg sys_faccessat
+
+ENTRY(compat_sys_pselect6_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # fd_set *
+ llgtr %r4,%r4 # fd_set *
+ llgtr %r5,%r5 # fd_set *
+ llgtr %r6,%r6 # struct timespec *
+ llgt %r0,164(%r15) # void *
+ stg %r0,160(%r15)
+ jg compat_sys_pselect6
+
+ENTRY(compat_sys_ppoll_wrapper)
+ llgtr %r2,%r2 # struct pollfd *
+ llgfr %r3,%r3 # unsigned int
+ llgtr %r4,%r4 # struct timespec *
+ llgtr %r5,%r5 # const sigset_t *
+ llgfr %r6,%r6 # size_t
+ jg compat_sys_ppoll
+
+ENTRY(sys_unshare_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ jg sys_unshare
+
+ENTRY(compat_sys_set_robust_list_wrapper)
+ llgtr %r2,%r2 # struct compat_robust_list_head *
+ llgfr %r3,%r3 # size_t
+ jg compat_sys_set_robust_list
+
+ENTRY(compat_sys_get_robust_list_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # compat_uptr_t_t *
+ llgtr %r4,%r4 # compat_size_t *
+ jg compat_sys_get_robust_list
+
+ENTRY(sys_splice_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # loff_t *
+ lgfr %r4,%r4 # int
+ llgtr %r5,%r5 # loff_t *
+ llgfr %r6,%r6 # size_t
+ llgf %r0,164(%r15) # unsigned int
+ stg %r0,160(%r15)
+ jg sys_splice
+
+ENTRY(sys_sync_file_range_wrapper)
+ lgfr %r2,%r2 # int
+ sllg %r3,%r3,32 # get high word of 64bit loff_t
+ or %r3,%r4 # get low word of 64bit loff_t
+ sllg %r4,%r5,32 # get high word of 64bit loff_t
+ or %r4,%r6 # get low word of 64bit loff_t
+ llgf %r5,164(%r15) # unsigned int
+ jg sys_sync_file_range
+
+ENTRY(sys_tee_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ llgfr %r4,%r4 # size_t
+ llgfr %r5,%r5 # unsigned int
+ jg sys_tee
+
+ENTRY(compat_sys_vmsplice_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # compat_iovec *
+ llgfr %r4,%r4 # unsigned int
+ llgfr %r5,%r5 # unsigned int
+ jg compat_sys_vmsplice
+
+ENTRY(sys_getcpu_wrapper)
+ llgtr %r2,%r2 # unsigned *
+ llgtr %r3,%r3 # unsigned *
+ llgtr %r4,%r4 # struct getcpu_cache *
+ jg sys_getcpu
+
+ENTRY(compat_sys_epoll_pwait_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # struct compat_epoll_event *
+ lgfr %r4,%r4 # int
+ lgfr %r5,%r5 # int
+ llgtr %r6,%r6 # compat_sigset_t *
+ llgf %r0,164(%r15) # compat_size_t
+ stg %r0,160(%r15)
+ jg compat_sys_epoll_pwait
+
+ENTRY(compat_sys_utimes_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # struct compat_timeval *
+ jg compat_sys_utimes
+
+ENTRY(compat_sys_utimensat_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgtr %r3,%r3 # char *
+ llgtr %r4,%r4 # struct compat_timespec *
+ lgfr %r5,%r5 # int
+ jg compat_sys_utimensat
+
+ENTRY(compat_sys_signalfd_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # compat_sigset_t *
+ llgfr %r4,%r4 # compat_size_t
+ jg compat_sys_signalfd
+
+ENTRY(sys_eventfd_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ jg sys_eventfd
+
+ENTRY(sys_fallocate_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ sllg %r4,%r4,32 # get high word of 64bit loff_t
+ lr %r4,%r5 # get low word of 64bit loff_t
+ sllg %r5,%r6,32 # get high word of 64bit loff_t
+ l %r5,164(%r15) # get low word of 64bit loff_t
+ jg sys_fallocate
+
+ENTRY(sys_timerfd_create_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ jg sys_timerfd_create
+
+ENTRY(compat_sys_timerfd_settime_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ llgtr %r4,%r4 # struct compat_itimerspec *
+ llgtr %r5,%r5 # struct compat_itimerspec *
+ jg compat_sys_timerfd_settime
+
+ENTRY(compat_sys_timerfd_gettime_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # struct compat_itimerspec *
+ jg compat_sys_timerfd_gettime
+
+ENTRY(compat_sys_signalfd4_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # compat_sigset_t *
+ llgfr %r4,%r4 # compat_size_t
+ lgfr %r5,%r5 # int
+ jg compat_sys_signalfd4
+
+ENTRY(sys_eventfd2_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ lgfr %r3,%r3 # int
+ jg sys_eventfd2
+
+ENTRY(sys_inotify_init1_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_inotify_init1
+
+ENTRY(sys_pipe2_wrapper)
+ llgtr %r2,%r2 # u32 *
+ lgfr %r3,%r3 # int
+ jg sys_pipe2 # branch to system call
+
+ENTRY(sys_dup3_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # unsigned int
+ lgfr %r4,%r4 # int
+ jg sys_dup3 # branch to system call
+
+ENTRY(sys_epoll_create1_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_epoll_create1 # branch to system call
+
+ENTRY(sys32_readahead_wrapper)
+ lgfr %r2,%r2 # int
+ llgfr %r3,%r3 # u32
+ llgfr %r4,%r4 # u32
+ lgfr %r5,%r5 # s32
+ jg sys32_readahead # branch to system call
+
+ENTRY(sys32_sendfile64_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ llgtr %r4,%r4 # compat_loff_t *
+ lgfr %r5,%r5 # s32
+ jg sys32_sendfile64 # branch to system call
+
+ENTRY(sys_tkill_wrapper)
+ lgfr %r2,%r2 # pid_t
+ lgfr %r3,%r3 # int
+ jg sys_tkill # branch to system call
+
+ENTRY(sys_tgkill_wrapper)
+ lgfr %r2,%r2 # pid_t
+ lgfr %r3,%r3 # pid_t
+ lgfr %r4,%r4 # int
+ jg sys_tgkill # branch to system call
+
+ENTRY(compat_sys_keyctl_wrapper)
+ llgfr %r2,%r2 # u32
+ llgfr %r3,%r3 # u32
+ llgfr %r4,%r4 # u32
+ llgfr %r5,%r5 # u32
+ llgfr %r6,%r6 # u32
+ jg compat_sys_keyctl # branch to system call
+
+ENTRY(compat_sys_preadv_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgtr %r3,%r3 # compat_iovec *
+ llgfr %r4,%r4 # unsigned long
+ llgfr %r5,%r5 # u32
+ llgfr %r6,%r6 # u32
+ jg compat_sys_preadv # branch to system call
+
+ENTRY(compat_sys_pwritev_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgtr %r3,%r3 # compat_iovec *
+ llgfr %r4,%r4 # unsigned long
+ llgfr %r5,%r5 # u32
+ llgfr %r6,%r6 # u32
+ jg compat_sys_pwritev # branch to system call
+
+ENTRY(compat_sys_rt_tgsigqueueinfo_wrapper)
+ lgfr %r2,%r2 # compat_pid_t
+ lgfr %r3,%r3 # compat_pid_t
+ lgfr %r4,%r4 # int
+ llgtr %r5,%r5 # struct compat_siginfo *
+ jg compat_sys_rt_tgsigqueueinfo_wrapper # branch to system call
+
+ENTRY(sys_perf_event_open_wrapper)
+ llgtr %r2,%r2 # const struct perf_event_attr *
+ lgfr %r3,%r3 # pid_t
+ lgfr %r4,%r4 # int
+ lgfr %r5,%r5 # int
+ llgfr %r6,%r6 # unsigned long
+ jg sys_perf_event_open # branch to system call
+
+ENTRY(sys_clone_wrapper)
+ llgfr %r2,%r2 # unsigned long
+ llgfr %r3,%r3 # unsigned long
+ llgtr %r4,%r4 # int *
+ llgtr %r5,%r5 # int *
+ jg sys_clone # branch to system call
+
+ENTRY(sys32_execve_wrapper)
+ llgtr %r2,%r2 # char *
+ llgtr %r3,%r3 # compat_uptr_t *
+ llgtr %r4,%r4 # compat_uptr_t *
+ jg sys32_execve # branch to system call
+
+ENTRY(sys_fanotify_init_wrapper)
+ llgfr %r2,%r2 # unsigned int
+ llgfr %r3,%r3 # unsigned int
+ jg sys_fanotify_init # branch to system call
+
+ENTRY(sys_fanotify_mark_wrapper)
+ lgfr %r2,%r2 # int
+ llgfr %r3,%r3 # unsigned int
+ sllg %r4,%r4,32 # get high word of 64bit mask
+ lr %r4,%r5 # get low word of 64bit mask
+ llgfr %r5,%r6 # unsigned int
+ llgt %r6,164(%r15) # char *
+ jg sys_fanotify_mark # branch to system call
+
+ENTRY(sys_prlimit64_wrapper)
+ lgfr %r2,%r2 # pid_t
+ llgfr %r3,%r3 # unsigned int
+ llgtr %r4,%r4 # const struct rlimit64 __user *
+ llgtr %r5,%r5 # struct rlimit64 __user *
+ jg sys_prlimit64 # branch to system call
+
+ENTRY(sys_name_to_handle_at_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # const char __user *
+ llgtr %r4,%r4 # struct file_handle __user *
+ llgtr %r5,%r5 # int __user *
+ lgfr %r6,%r6 # int
+ jg sys_name_to_handle_at
+
+ENTRY(compat_sys_open_by_handle_at_wrapper)
+ lgfr %r2,%r2 # int
+ llgtr %r3,%r3 # struct file_handle __user *
+ lgfr %r4,%r4 # int
+ jg compat_sys_open_by_handle_at
+
+ENTRY(compat_sys_clock_adjtime_wrapper)
+ lgfr %r2,%r2 # clockid_t (int)
+ llgtr %r3,%r3 # struct compat_timex __user *
+ jg compat_sys_clock_adjtime
+
+ENTRY(sys_syncfs_wrapper)
+ lgfr %r2,%r2 # int
+ jg sys_syncfs
+
+ENTRY(sys_setns_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ jg sys_setns
+
+ENTRY(compat_sys_process_vm_readv_wrapper)
+ lgfr %r2,%r2 # compat_pid_t
+ llgtr %r3,%r3 # struct compat_iovec __user *
+ llgfr %r4,%r4 # unsigned long
+ llgtr %r5,%r5 # struct compat_iovec __user *
+ llgfr %r6,%r6 # unsigned long
+ llgf %r0,164(%r15) # unsigned long
+ stg %r0,160(%r15)
+ jg sys_process_vm_readv
+
+ENTRY(compat_sys_process_vm_writev_wrapper)
+ lgfr %r2,%r2 # compat_pid_t
+ llgtr %r3,%r3 # struct compat_iovec __user *
+ llgfr %r4,%r4 # unsigned long
+ llgtr %r5,%r5 # struct compat_iovec __user *
+ llgfr %r6,%r6 # unsigned long
+ llgf %r0,164(%r15) # unsigned long
+ stg %r0,160(%r15)
+ jg sys_process_vm_writev
diff --git a/arch/s390/kernel/cpcmd.c b/arch/s390/kernel/cpcmd.c
new file mode 100644
index 00000000..e3dd886e
--- /dev/null
+++ b/arch/s390/kernel/cpcmd.c
@@ -0,0 +1,125 @@
+/*
+ * arch/s390/kernel/cpcmd.c
+ *
+ * S390 version
+ * Copyright IBM Corp. 1999,2007
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ * Christian Borntraeger (cborntra@de.ibm.com),
+ */
+
+#define KMSG_COMPONENT "cpcmd"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <asm/ebcdic.h>
+#include <asm/cpcmd.h>
+#include <asm/io.h>
+
+static DEFINE_SPINLOCK(cpcmd_lock);
+static char cpcmd_buf[241];
+
+static int diag8_noresponse(int cmdlen)
+{
+ register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf;
+ register unsigned long reg3 asm ("3") = cmdlen;
+
+ asm volatile(
+#ifndef CONFIG_64BIT
+ " diag %1,%0,0x8\n"
+#else /* CONFIG_64BIT */
+ " sam31\n"
+ " diag %1,%0,0x8\n"
+ " sam64\n"
+#endif /* CONFIG_64BIT */
+ : "+d" (reg3) : "d" (reg2) : "cc");
+ return reg3;
+}
+
+static int diag8_response(int cmdlen, char *response, int *rlen)
+{
+ register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf;
+ register unsigned long reg3 asm ("3") = (addr_t) response;
+ register unsigned long reg4 asm ("4") = cmdlen | 0x40000000L;
+ register unsigned long reg5 asm ("5") = *rlen;
+
+ asm volatile(
+#ifndef CONFIG_64BIT
+ " diag %2,%0,0x8\n"
+ " brc 8,1f\n"
+ " ar %1,%4\n"
+#else /* CONFIG_64BIT */
+ " sam31\n"
+ " diag %2,%0,0x8\n"
+ " sam64\n"
+ " brc 8,1f\n"
+ " agr %1,%4\n"
+#endif /* CONFIG_64BIT */
+ "1:\n"
+ : "+d" (reg4), "+d" (reg5)
+ : "d" (reg2), "d" (reg3), "d" (*rlen) : "cc");
+ *rlen = reg5;
+ return reg4;
+}
+
+/*
+ * __cpcmd has some restrictions over cpcmd
+ * - the response buffer must reside below 2GB (if any)
+ * - __cpcmd is unlocked and therefore not SMP-safe
+ */
+int __cpcmd(const char *cmd, char *response, int rlen, int *response_code)
+{
+ int cmdlen;
+ int rc;
+ int response_len;
+
+ cmdlen = strlen(cmd);
+ BUG_ON(cmdlen > 240);
+ memcpy(cpcmd_buf, cmd, cmdlen);
+ ASCEBC(cpcmd_buf, cmdlen);
+
+ if (response) {
+ memset(response, 0, rlen);
+ response_len = rlen;
+ rc = diag8_response(cmdlen, response, &rlen);
+ EBCASC(response, response_len);
+ } else {
+ rc = diag8_noresponse(cmdlen);
+ }
+ if (response_code)
+ *response_code = rc;
+ return rlen;
+}
+EXPORT_SYMBOL(__cpcmd);
+
+int cpcmd(const char *cmd, char *response, int rlen, int *response_code)
+{
+ char *lowbuf;
+ int len;
+ unsigned long flags;
+
+ if ((virt_to_phys(response) != (unsigned long) response) ||
+ (((unsigned long)response + rlen) >> 31)) {
+ lowbuf = kmalloc(rlen, GFP_KERNEL | GFP_DMA);
+ if (!lowbuf) {
+ pr_warning("The cpcmd kernel function failed to "
+ "allocate a response buffer\n");
+ return -ENOMEM;
+ }
+ spin_lock_irqsave(&cpcmd_lock, flags);
+ len = __cpcmd(cmd, lowbuf, rlen, response_code);
+ spin_unlock_irqrestore(&cpcmd_lock, flags);
+ memcpy(response, lowbuf, rlen);
+ kfree(lowbuf);
+ } else {
+ spin_lock_irqsave(&cpcmd_lock, flags);
+ len = __cpcmd(cmd, response, rlen, response_code);
+ spin_unlock_irqrestore(&cpcmd_lock, flags);
+ }
+ return len;
+}
+EXPORT_SYMBOL(cpcmd);
diff --git a/arch/s390/kernel/crash.c b/arch/s390/kernel/crash.c
new file mode 100644
index 00000000..8cc7c9fa
--- /dev/null
+++ b/arch/s390/kernel/crash.c
@@ -0,0 +1,16 @@
+/*
+ * arch/s390/kernel/crash.c
+ *
+ * (C) Copyright IBM Corp. 2005
+ *
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ *
+ */
+
+#include <linux/threads.h>
+#include <linux/kexec.h>
+#include <linux/reboot.h>
+
+void machine_crash_shutdown(struct pt_regs *regs)
+{
+}
diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c
new file mode 100644
index 00000000..cc1172b2
--- /dev/null
+++ b/arch/s390/kernel/crash_dump.c
@@ -0,0 +1,442 @@
+/*
+ * S390 kdump implementation
+ *
+ * Copyright IBM Corp. 2011
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#include <linux/crash_dump.h>
+#include <asm/lowcore.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gfp.h>
+#include <linux/slab.h>
+#include <linux/bootmem.h>
+#include <linux/elf.h>
+#include <asm/ipl.h>
+#include <asm/os_info.h>
+
+#define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
+#define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
+#define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y))))
+
+/*
+ * Copy one page from "oldmem"
+ *
+ * For the kdump reserved memory this functions performs a swap operation:
+ * - [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE] is mapped to [0 - OLDMEM_SIZE].
+ * - [0 - OLDMEM_SIZE] is mapped to [OLDMEM_BASE - OLDMEM_BASE + OLDMEM_SIZE]
+ */
+ssize_t copy_oldmem_page(unsigned long pfn, char *buf,
+ size_t csize, unsigned long offset, int userbuf)
+{
+ unsigned long src;
+
+ if (!csize)
+ return 0;
+
+ src = (pfn << PAGE_SHIFT) + offset;
+ if (src < OLDMEM_SIZE)
+ src += OLDMEM_BASE;
+ else if (src > OLDMEM_BASE &&
+ src < OLDMEM_BASE + OLDMEM_SIZE)
+ src -= OLDMEM_BASE;
+ if (userbuf)
+ copy_to_user_real((void __force __user *) buf, (void *) src,
+ csize);
+ else
+ memcpy_real(buf, (void *) src, csize);
+ return csize;
+}
+
+/*
+ * Copy memory from old kernel
+ */
+int copy_from_oldmem(void *dest, void *src, size_t count)
+{
+ unsigned long copied = 0;
+ int rc;
+
+ if ((unsigned long) src < OLDMEM_SIZE) {
+ copied = min(count, OLDMEM_SIZE - (unsigned long) src);
+ rc = memcpy_real(dest, src + OLDMEM_BASE, copied);
+ if (rc)
+ return rc;
+ }
+ return memcpy_real(dest + copied, src + copied, count - copied);
+}
+
+/*
+ * Alloc memory and panic in case of ENOMEM
+ */
+static void *kzalloc_panic(int len)
+{
+ void *rc;
+
+ rc = kzalloc(len, GFP_KERNEL);
+ if (!rc)
+ panic("s390 kdump kzalloc (%d) failed", len);
+ return rc;
+}
+
+/*
+ * Get memory layout and create hole for oldmem
+ */
+static struct mem_chunk *get_memory_layout(void)
+{
+ struct mem_chunk *chunk_array;
+
+ chunk_array = kzalloc_panic(MEMORY_CHUNKS * sizeof(struct mem_chunk));
+ detect_memory_layout(chunk_array);
+ create_mem_hole(chunk_array, OLDMEM_BASE, OLDMEM_SIZE, CHUNK_CRASHK);
+ return chunk_array;
+}
+
+/*
+ * Initialize ELF note
+ */
+static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
+ const char *name)
+{
+ Elf64_Nhdr *note;
+ u64 len;
+
+ note = (Elf64_Nhdr *)buf;
+ note->n_namesz = strlen(name) + 1;
+ note->n_descsz = d_len;
+ note->n_type = type;
+ len = sizeof(Elf64_Nhdr);
+
+ memcpy(buf + len, name, note->n_namesz);
+ len = roundup(len + note->n_namesz, 4);
+
+ memcpy(buf + len, desc, note->n_descsz);
+ len = roundup(len + note->n_descsz, 4);
+
+ return PTR_ADD(buf, len);
+}
+
+/*
+ * Initialize prstatus note
+ */
+static void *nt_prstatus(void *ptr, struct save_area *sa)
+{
+ struct elf_prstatus nt_prstatus;
+ static int cpu_nr = 1;
+
+ memset(&nt_prstatus, 0, sizeof(nt_prstatus));
+ memcpy(&nt_prstatus.pr_reg.gprs, sa->gp_regs, sizeof(sa->gp_regs));
+ memcpy(&nt_prstatus.pr_reg.psw, sa->psw, sizeof(sa->psw));
+ memcpy(&nt_prstatus.pr_reg.acrs, sa->acc_regs, sizeof(sa->acc_regs));
+ nt_prstatus.pr_pid = cpu_nr;
+ cpu_nr++;
+
+ return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus),
+ "CORE");
+}
+
+/*
+ * Initialize fpregset (floating point) note
+ */
+static void *nt_fpregset(void *ptr, struct save_area *sa)
+{
+ elf_fpregset_t nt_fpregset;
+
+ memset(&nt_fpregset, 0, sizeof(nt_fpregset));
+ memcpy(&nt_fpregset.fpc, &sa->fp_ctrl_reg, sizeof(sa->fp_ctrl_reg));
+ memcpy(&nt_fpregset.fprs, &sa->fp_regs, sizeof(sa->fp_regs));
+
+ return nt_init(ptr, NT_PRFPREG, &nt_fpregset, sizeof(nt_fpregset),
+ "CORE");
+}
+
+/*
+ * Initialize timer note
+ */
+static void *nt_s390_timer(void *ptr, struct save_area *sa)
+{
+ return nt_init(ptr, NT_S390_TIMER, &sa->timer, sizeof(sa->timer),
+ KEXEC_CORE_NOTE_NAME);
+}
+
+/*
+ * Initialize TOD clock comparator note
+ */
+static void *nt_s390_tod_cmp(void *ptr, struct save_area *sa)
+{
+ return nt_init(ptr, NT_S390_TODCMP, &sa->clk_cmp,
+ sizeof(sa->clk_cmp), KEXEC_CORE_NOTE_NAME);
+}
+
+/*
+ * Initialize TOD programmable register note
+ */
+static void *nt_s390_tod_preg(void *ptr, struct save_area *sa)
+{
+ return nt_init(ptr, NT_S390_TODPREG, &sa->tod_reg,
+ sizeof(sa->tod_reg), KEXEC_CORE_NOTE_NAME);
+}
+
+/*
+ * Initialize control register note
+ */
+static void *nt_s390_ctrs(void *ptr, struct save_area *sa)
+{
+ return nt_init(ptr, NT_S390_CTRS, &sa->ctrl_regs,
+ sizeof(sa->ctrl_regs), KEXEC_CORE_NOTE_NAME);
+}
+
+/*
+ * Initialize prefix register note
+ */
+static void *nt_s390_prefix(void *ptr, struct save_area *sa)
+{
+ return nt_init(ptr, NT_S390_PREFIX, &sa->pref_reg,
+ sizeof(sa->pref_reg), KEXEC_CORE_NOTE_NAME);
+}
+
+/*
+ * Fill ELF notes for one CPU with save area registers
+ */
+void *fill_cpu_elf_notes(void *ptr, struct save_area *sa)
+{
+ ptr = nt_prstatus(ptr, sa);
+ ptr = nt_fpregset(ptr, sa);
+ ptr = nt_s390_timer(ptr, sa);
+ ptr = nt_s390_tod_cmp(ptr, sa);
+ ptr = nt_s390_tod_preg(ptr, sa);
+ ptr = nt_s390_ctrs(ptr, sa);
+ ptr = nt_s390_prefix(ptr, sa);
+ return ptr;
+}
+
+/*
+ * Initialize prpsinfo note (new kernel)
+ */
+static void *nt_prpsinfo(void *ptr)
+{
+ struct elf_prpsinfo prpsinfo;
+
+ memset(&prpsinfo, 0, sizeof(prpsinfo));
+ prpsinfo.pr_sname = 'R';
+ strcpy(prpsinfo.pr_fname, "vmlinux");
+ return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo),
+ KEXEC_CORE_NOTE_NAME);
+}
+
+/*
+ * Get vmcoreinfo using lowcore->vmcore_info (new kernel)
+ */
+static void *get_vmcoreinfo_old(unsigned long *size)
+{
+ char nt_name[11], *vmcoreinfo;
+ Elf64_Nhdr note;
+ void *addr;
+
+ if (copy_from_oldmem(&addr, &S390_lowcore.vmcore_info, sizeof(addr)))
+ return NULL;
+ memset(nt_name, 0, sizeof(nt_name));
+ if (copy_from_oldmem(&note, addr, sizeof(note)))
+ return NULL;
+ if (copy_from_oldmem(nt_name, addr + sizeof(note), sizeof(nt_name) - 1))
+ return NULL;
+ if (strcmp(nt_name, "VMCOREINFO") != 0)
+ return NULL;
+ vmcoreinfo = kzalloc_panic(note.n_descsz);
+ if (copy_from_oldmem(vmcoreinfo, addr + 24, note.n_descsz))
+ return NULL;
+ *size = note.n_descsz;
+ return vmcoreinfo;
+}
+
+/*
+ * Initialize vmcoreinfo note (new kernel)
+ */
+static void *nt_vmcoreinfo(void *ptr)
+{
+ unsigned long size;
+ void *vmcoreinfo;
+
+ vmcoreinfo = os_info_old_entry(OS_INFO_VMCOREINFO, &size);
+ if (!vmcoreinfo)
+ vmcoreinfo = get_vmcoreinfo_old(&size);
+ if (!vmcoreinfo)
+ return ptr;
+ return nt_init(ptr, 0, vmcoreinfo, size, "VMCOREINFO");
+}
+
+/*
+ * Initialize ELF header (new kernel)
+ */
+static void *ehdr_init(Elf64_Ehdr *ehdr, int mem_chunk_cnt)
+{
+ memset(ehdr, 0, sizeof(*ehdr));
+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
+ ehdr->e_ident[EI_CLASS] = ELFCLASS64;
+ ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
+ memset(ehdr->e_ident + EI_PAD, 0, EI_NIDENT - EI_PAD);
+ ehdr->e_type = ET_CORE;
+ ehdr->e_machine = EM_S390;
+ ehdr->e_version = EV_CURRENT;
+ ehdr->e_phoff = sizeof(Elf64_Ehdr);
+ ehdr->e_ehsize = sizeof(Elf64_Ehdr);
+ ehdr->e_phentsize = sizeof(Elf64_Phdr);
+ ehdr->e_phnum = mem_chunk_cnt + 1;
+ return ehdr + 1;
+}
+
+/*
+ * Return CPU count for ELF header (new kernel)
+ */
+static int get_cpu_cnt(void)
+{
+ int i, cpus = 0;
+
+ for (i = 0; zfcpdump_save_areas[i]; i++) {
+ if (zfcpdump_save_areas[i]->pref_reg == 0)
+ continue;
+ cpus++;
+ }
+ return cpus;
+}
+
+/*
+ * Return memory chunk count for ELF header (new kernel)
+ */
+static int get_mem_chunk_cnt(void)
+{
+ struct mem_chunk *chunk_array, *mem_chunk;
+ int i, cnt = 0;
+
+ chunk_array = get_memory_layout();
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ mem_chunk = &chunk_array[i];
+ if (chunk_array[i].type != CHUNK_READ_WRITE &&
+ chunk_array[i].type != CHUNK_READ_ONLY)
+ continue;
+ if (mem_chunk->size == 0)
+ continue;
+ cnt++;
+ }
+ kfree(chunk_array);
+ return cnt;
+}
+
+/*
+ * Relocate pointer in order to allow vmcore code access the data
+ */
+static inline unsigned long relocate(unsigned long addr)
+{
+ return OLDMEM_BASE + addr;
+}
+
+/*
+ * Initialize ELF loads (new kernel)
+ */
+static int loads_init(Elf64_Phdr *phdr, u64 loads_offset)
+{
+ struct mem_chunk *chunk_array, *mem_chunk;
+ int i;
+
+ chunk_array = get_memory_layout();
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ mem_chunk = &chunk_array[i];
+ if (mem_chunk->size == 0)
+ break;
+ if (chunk_array[i].type != CHUNK_READ_WRITE &&
+ chunk_array[i].type != CHUNK_READ_ONLY)
+ continue;
+ else
+ phdr->p_filesz = mem_chunk->size;
+ phdr->p_type = PT_LOAD;
+ phdr->p_offset = mem_chunk->addr;
+ phdr->p_vaddr = mem_chunk->addr;
+ phdr->p_paddr = mem_chunk->addr;
+ phdr->p_memsz = mem_chunk->size;
+ phdr->p_flags = PF_R | PF_W | PF_X;
+ phdr->p_align = PAGE_SIZE;
+ phdr++;
+ }
+ kfree(chunk_array);
+ return i;
+}
+
+/*
+ * Initialize notes (new kernel)
+ */
+static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
+{
+ struct save_area *sa;
+ void *ptr_start = ptr;
+ int i;
+
+ ptr = nt_prpsinfo(ptr);
+
+ for (i = 0; zfcpdump_save_areas[i]; i++) {
+ sa = zfcpdump_save_areas[i];
+ if (sa->pref_reg == 0)
+ continue;
+ ptr = fill_cpu_elf_notes(ptr, sa);
+ }
+ ptr = nt_vmcoreinfo(ptr);
+ memset(phdr, 0, sizeof(*phdr));
+ phdr->p_type = PT_NOTE;
+ phdr->p_offset = relocate(notes_offset);
+ phdr->p_filesz = (unsigned long) PTR_SUB(ptr, ptr_start);
+ phdr->p_memsz = phdr->p_filesz;
+ return ptr;
+}
+
+/*
+ * Create ELF core header (new kernel)
+ */
+static void s390_elf_corehdr_create(char **elfcorebuf, size_t *elfcorebuf_sz)
+{
+ Elf64_Phdr *phdr_notes, *phdr_loads;
+ int mem_chunk_cnt;
+ void *ptr, *hdr;
+ u32 alloc_size;
+ u64 hdr_off;
+
+ mem_chunk_cnt = get_mem_chunk_cnt();
+
+ alloc_size = 0x1000 + get_cpu_cnt() * 0x300 +
+ mem_chunk_cnt * sizeof(Elf64_Phdr);
+ hdr = kzalloc_panic(alloc_size);
+ /* Init elf header */
+ ptr = ehdr_init(hdr, mem_chunk_cnt);
+ /* Init program headers */
+ phdr_notes = ptr;
+ ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr));
+ phdr_loads = ptr;
+ ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr) * mem_chunk_cnt);
+ /* Init notes */
+ hdr_off = PTR_DIFF(ptr, hdr);
+ ptr = notes_init(phdr_notes, ptr, ((unsigned long) hdr) + hdr_off);
+ /* Init loads */
+ hdr_off = PTR_DIFF(ptr, hdr);
+ loads_init(phdr_loads, ((unsigned long) hdr) + hdr_off);
+ *elfcorebuf_sz = hdr_off;
+ *elfcorebuf = (void *) relocate((unsigned long) hdr);
+ BUG_ON(*elfcorebuf_sz > alloc_size);
+}
+
+/*
+ * Create kdump ELF core header in new kernel, if it has not been passed via
+ * the "elfcorehdr" kernel parameter
+ */
+static int setup_kdump_elfcorehdr(void)
+{
+ size_t elfcorebuf_sz;
+ char *elfcorebuf;
+
+ if (!OLDMEM_BASE || is_kdump_kernel())
+ return -EINVAL;
+ s390_elf_corehdr_create(&elfcorebuf, &elfcorebuf_sz);
+ elfcorehdr_addr = (unsigned long long) elfcorebuf;
+ elfcorehdr_size = elfcorebuf_sz;
+ return 0;
+}
+
+subsys_initcall(setup_kdump_elfcorehdr);
diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
new file mode 100644
index 00000000..19e5e9eb
--- /dev/null
+++ b/arch/s390/kernel/debug.c
@@ -0,0 +1,1566 @@
+/*
+ * arch/s390/kernel/debug.c
+ * S/390 debug facility
+ *
+ * Copyright IBM Corp. 1999, 2012
+ *
+ * Author(s): Michael Holzheu (holzheu@de.ibm.com),
+ * Holger Smolinski (Holger.Smolinski@de.ibm.com)
+ *
+ * Bugreports to: <Linux390@de.ibm.com>
+ */
+
+#define KMSG_COMPONENT "s390dbf"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/sysctl.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+
+#include <asm/debug.h>
+
+#define DEBUG_PROLOG_ENTRY -1
+
+#define ALL_AREAS 0 /* copy all debug areas */
+#define NO_AREAS 1 /* copy no debug areas */
+
+/* typedefs */
+
+typedef struct file_private_info {
+ loff_t offset; /* offset of last read in file */
+ int act_area; /* number of last formated area */
+ int act_page; /* act page in given area */
+ int act_entry; /* last formated entry (offset */
+ /* relative to beginning of last */
+ /* formated page) */
+ size_t act_entry_offset; /* up to this offset we copied */
+ /* in last read the last formated */
+ /* entry to userland */
+ char temp_buf[2048]; /* buffer for output */
+ debug_info_t *debug_info_org; /* original debug information */
+ debug_info_t *debug_info_snap; /* snapshot of debug information */
+ struct debug_view *view; /* used view of debug info */
+} file_private_info_t;
+
+typedef struct
+{
+ char *string;
+ /*
+ * This assumes that all args are converted into longs
+ * on L/390 this is the case for all types of parameter
+ * except of floats, and long long (32 bit)
+ *
+ */
+ long args[0];
+} debug_sprintf_entry_t;
+
+
+/* internal function prototyes */
+
+static int debug_init(void);
+static ssize_t debug_output(struct file *file, char __user *user_buf,
+ size_t user_len, loff_t * offset);
+static ssize_t debug_input(struct file *file, const char __user *user_buf,
+ size_t user_len, loff_t * offset);
+static int debug_open(struct inode *inode, struct file *file);
+static int debug_close(struct inode *inode, struct file *file);
+static debug_info_t *debug_info_create(const char *name, int pages_per_area,
+ int nr_areas, int buf_size, umode_t mode);
+static void debug_info_get(debug_info_t *);
+static void debug_info_put(debug_info_t *);
+static int debug_prolog_level_fn(debug_info_t * id,
+ struct debug_view *view, char *out_buf);
+static int debug_input_level_fn(debug_info_t * id, struct debug_view *view,
+ struct file *file, const char __user *user_buf,
+ size_t user_buf_size, loff_t * offset);
+static int debug_prolog_pages_fn(debug_info_t * id,
+ struct debug_view *view, char *out_buf);
+static int debug_input_pages_fn(debug_info_t * id, struct debug_view *view,
+ struct file *file, const char __user *user_buf,
+ size_t user_buf_size, loff_t * offset);
+static int debug_input_flush_fn(debug_info_t * id, struct debug_view *view,
+ struct file *file, const char __user *user_buf,
+ size_t user_buf_size, loff_t * offset);
+static int debug_hex_ascii_format_fn(debug_info_t * id, struct debug_view *view,
+ char *out_buf, const char *in_buf);
+static int debug_raw_format_fn(debug_info_t * id,
+ struct debug_view *view, char *out_buf,
+ const char *in_buf);
+static int debug_raw_header_fn(debug_info_t * id, struct debug_view *view,
+ int area, debug_entry_t * entry, char *out_buf);
+
+static int debug_sprintf_format_fn(debug_info_t * id, struct debug_view *view,
+ char *out_buf, debug_sprintf_entry_t *curr_event);
+
+/* globals */
+
+struct debug_view debug_raw_view = {
+ "raw",
+ NULL,
+ &debug_raw_header_fn,
+ &debug_raw_format_fn,
+ NULL,
+ NULL
+};
+
+struct debug_view debug_hex_ascii_view = {
+ "hex_ascii",
+ NULL,
+ &debug_dflt_header_fn,
+ &debug_hex_ascii_format_fn,
+ NULL,
+ NULL
+};
+
+static struct debug_view debug_level_view = {
+ "level",
+ &debug_prolog_level_fn,
+ NULL,
+ NULL,
+ &debug_input_level_fn,
+ NULL
+};
+
+static struct debug_view debug_pages_view = {
+ "pages",
+ &debug_prolog_pages_fn,
+ NULL,
+ NULL,
+ &debug_input_pages_fn,
+ NULL
+};
+
+static struct debug_view debug_flush_view = {
+ "flush",
+ NULL,
+ NULL,
+ NULL,
+ &debug_input_flush_fn,
+ NULL
+};
+
+struct debug_view debug_sprintf_view = {
+ "sprintf",
+ NULL,
+ &debug_dflt_header_fn,
+ (debug_format_proc_t*)&debug_sprintf_format_fn,
+ NULL,
+ NULL
+};
+
+/* used by dump analysis tools to determine version of debug feature */
+static unsigned int __used debug_feature_version = __DEBUG_FEATURE_VERSION;
+
+/* static globals */
+
+static debug_info_t *debug_area_first = NULL;
+static debug_info_t *debug_area_last = NULL;
+static DEFINE_MUTEX(debug_mutex);
+
+static int initialized;
+static int debug_critical;
+
+static const struct file_operations debug_file_ops = {
+ .owner = THIS_MODULE,
+ .read = debug_output,
+ .write = debug_input,
+ .open = debug_open,
+ .release = debug_close,
+ .llseek = no_llseek,
+};
+
+static struct dentry *debug_debugfs_root_entry;
+
+/* functions */
+
+/*
+ * debug_areas_alloc
+ * - Debug areas are implemented as a threedimensonal array:
+ * areas[areanumber][pagenumber][pageoffset]
+ */
+
+static debug_entry_t***
+debug_areas_alloc(int pages_per_area, int nr_areas)
+{
+ debug_entry_t*** areas;
+ int i,j;
+
+ areas = kmalloc(nr_areas *
+ sizeof(debug_entry_t**),
+ GFP_KERNEL);
+ if (!areas)
+ goto fail_malloc_areas;
+ for (i = 0; i < nr_areas; i++) {
+ areas[i] = kmalloc(pages_per_area *
+ sizeof(debug_entry_t*),GFP_KERNEL);
+ if (!areas[i]) {
+ goto fail_malloc_areas2;
+ }
+ for(j = 0; j < pages_per_area; j++) {
+ areas[i][j] = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if(!areas[i][j]) {
+ for(j--; j >=0 ; j--) {
+ kfree(areas[i][j]);
+ }
+ kfree(areas[i]);
+ goto fail_malloc_areas2;
+ }
+ }
+ }
+ return areas;
+
+fail_malloc_areas2:
+ for(i--; i >= 0; i--){
+ for(j=0; j < pages_per_area;j++){
+ kfree(areas[i][j]);
+ }
+ kfree(areas[i]);
+ }
+ kfree(areas);
+fail_malloc_areas:
+ return NULL;
+
+}
+
+
+/*
+ * debug_info_alloc
+ * - alloc new debug-info
+ */
+
+static debug_info_t*
+debug_info_alloc(const char *name, int pages_per_area, int nr_areas,
+ int buf_size, int level, int mode)
+{
+ debug_info_t* rc;
+
+ /* alloc everything */
+
+ rc = kmalloc(sizeof(debug_info_t), GFP_KERNEL);
+ if(!rc)
+ goto fail_malloc_rc;
+ rc->active_entries = kcalloc(nr_areas, sizeof(int), GFP_KERNEL);
+ if(!rc->active_entries)
+ goto fail_malloc_active_entries;
+ rc->active_pages = kcalloc(nr_areas, sizeof(int), GFP_KERNEL);
+ if(!rc->active_pages)
+ goto fail_malloc_active_pages;
+ if((mode == ALL_AREAS) && (pages_per_area != 0)){
+ rc->areas = debug_areas_alloc(pages_per_area, nr_areas);
+ if(!rc->areas)
+ goto fail_malloc_areas;
+ } else {
+ rc->areas = NULL;
+ }
+
+ /* initialize members */
+
+ spin_lock_init(&rc->lock);
+ rc->pages_per_area = pages_per_area;
+ rc->nr_areas = nr_areas;
+ rc->active_area = 0;
+ rc->level = level;
+ rc->buf_size = buf_size;
+ rc->entry_size = sizeof(debug_entry_t) + buf_size;
+ strlcpy(rc->name, name, sizeof(rc->name));
+ memset(rc->views, 0, DEBUG_MAX_VIEWS * sizeof(struct debug_view *));
+ memset(rc->debugfs_entries, 0 ,DEBUG_MAX_VIEWS *
+ sizeof(struct dentry*));
+ atomic_set(&(rc->ref_count), 0);
+
+ return rc;
+
+fail_malloc_areas:
+ kfree(rc->active_pages);
+fail_malloc_active_pages:
+ kfree(rc->active_entries);
+fail_malloc_active_entries:
+ kfree(rc);
+fail_malloc_rc:
+ return NULL;
+}
+
+/*
+ * debug_areas_free
+ * - free all debug areas
+ */
+
+static void
+debug_areas_free(debug_info_t* db_info)
+{
+ int i,j;
+
+ if(!db_info->areas)
+ return;
+ for (i = 0; i < db_info->nr_areas; i++) {
+ for(j = 0; j < db_info->pages_per_area; j++) {
+ kfree(db_info->areas[i][j]);
+ }
+ kfree(db_info->areas[i]);
+ }
+ kfree(db_info->areas);
+ db_info->areas = NULL;
+}
+
+/*
+ * debug_info_free
+ * - free memory debug-info
+ */
+
+static void
+debug_info_free(debug_info_t* db_info){
+ debug_areas_free(db_info);
+ kfree(db_info->active_entries);
+ kfree(db_info->active_pages);
+ kfree(db_info);
+}
+
+/*
+ * debug_info_create
+ * - create new debug-info
+ */
+
+static debug_info_t*
+debug_info_create(const char *name, int pages_per_area, int nr_areas,
+ int buf_size, umode_t mode)
+{
+ debug_info_t* rc;
+
+ rc = debug_info_alloc(name, pages_per_area, nr_areas, buf_size,
+ DEBUG_DEFAULT_LEVEL, ALL_AREAS);
+ if(!rc)
+ goto out;
+
+ rc->mode = mode & ~S_IFMT;
+
+ /* create root directory */
+ rc->debugfs_root_entry = debugfs_create_dir(rc->name,
+ debug_debugfs_root_entry);
+
+ /* append new element to linked list */
+ if (!debug_area_first) {
+ /* first element in list */
+ debug_area_first = rc;
+ rc->prev = NULL;
+ } else {
+ /* append element to end of list */
+ debug_area_last->next = rc;
+ rc->prev = debug_area_last;
+ }
+ debug_area_last = rc;
+ rc->next = NULL;
+
+ debug_info_get(rc);
+out:
+ return rc;
+}
+
+/*
+ * debug_info_copy
+ * - copy debug-info
+ */
+
+static debug_info_t*
+debug_info_copy(debug_info_t* in, int mode)
+{
+ int i,j;
+ debug_info_t* rc;
+ unsigned long flags;
+
+ /* get a consistent copy of the debug areas */
+ do {
+ rc = debug_info_alloc(in->name, in->pages_per_area,
+ in->nr_areas, in->buf_size, in->level, mode);
+ spin_lock_irqsave(&in->lock, flags);
+ if(!rc)
+ goto out;
+ /* has something changed in the meantime ? */
+ if((rc->pages_per_area == in->pages_per_area) &&
+ (rc->nr_areas == in->nr_areas)) {
+ break;
+ }
+ spin_unlock_irqrestore(&in->lock, flags);
+ debug_info_free(rc);
+ } while (1);
+
+ if (mode == NO_AREAS)
+ goto out;
+
+ for(i = 0; i < in->nr_areas; i++){
+ for(j = 0; j < in->pages_per_area; j++) {
+ memcpy(rc->areas[i][j], in->areas[i][j],PAGE_SIZE);
+ }
+ }
+out:
+ spin_unlock_irqrestore(&in->lock, flags);
+ return rc;
+}
+
+/*
+ * debug_info_get
+ * - increments reference count for debug-info
+ */
+
+static void
+debug_info_get(debug_info_t * db_info)
+{
+ if (db_info)
+ atomic_inc(&db_info->ref_count);
+}
+
+/*
+ * debug_info_put:
+ * - decreases reference count for debug-info and frees it if necessary
+ */
+
+static void
+debug_info_put(debug_info_t *db_info)
+{
+ int i;
+
+ if (!db_info)
+ return;
+ if (atomic_dec_and_test(&db_info->ref_count)) {
+ for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
+ if (!db_info->views[i])
+ continue;
+ debugfs_remove(db_info->debugfs_entries[i]);
+ }
+ debugfs_remove(db_info->debugfs_root_entry);
+ if(db_info == debug_area_first)
+ debug_area_first = db_info->next;
+ if(db_info == debug_area_last)
+ debug_area_last = db_info->prev;
+ if(db_info->prev) db_info->prev->next = db_info->next;
+ if(db_info->next) db_info->next->prev = db_info->prev;
+ debug_info_free(db_info);
+ }
+}
+
+/*
+ * debug_format_entry:
+ * - format one debug entry and return size of formated data
+ */
+
+static int
+debug_format_entry(file_private_info_t *p_info)
+{
+ debug_info_t *id_snap = p_info->debug_info_snap;
+ struct debug_view *view = p_info->view;
+ debug_entry_t *act_entry;
+ size_t len = 0;
+ if(p_info->act_entry == DEBUG_PROLOG_ENTRY){
+ /* print prolog */
+ if (view->prolog_proc)
+ len += view->prolog_proc(id_snap,view,p_info->temp_buf);
+ goto out;
+ }
+ if (!id_snap->areas) /* this is true, if we have a prolog only view */
+ goto out; /* or if 'pages_per_area' is 0 */
+ act_entry = (debug_entry_t *) ((char*)id_snap->areas[p_info->act_area]
+ [p_info->act_page] + p_info->act_entry);
+
+ if (act_entry->id.stck == 0LL)
+ goto out; /* empty entry */
+ if (view->header_proc)
+ len += view->header_proc(id_snap, view, p_info->act_area,
+ act_entry, p_info->temp_buf + len);
+ if (view->format_proc)
+ len += view->format_proc(id_snap, view, p_info->temp_buf + len,
+ DEBUG_DATA(act_entry));
+out:
+ return len;
+}
+
+/*
+ * debug_next_entry:
+ * - goto next entry in p_info
+ */
+
+static inline int
+debug_next_entry(file_private_info_t *p_info)
+{
+ debug_info_t *id;
+
+ id = p_info->debug_info_snap;
+ if(p_info->act_entry == DEBUG_PROLOG_ENTRY){
+ p_info->act_entry = 0;
+ p_info->act_page = 0;
+ goto out;
+ }
+ if(!id->areas)
+ return 1;
+ p_info->act_entry += id->entry_size;
+ /* switch to next page, if we reached the end of the page */
+ if (p_info->act_entry > (PAGE_SIZE - id->entry_size)){
+ /* next page */
+ p_info->act_entry = 0;
+ p_info->act_page += 1;
+ if((p_info->act_page % id->pages_per_area) == 0) {
+ /* next area */
+ p_info->act_area++;
+ p_info->act_page=0;
+ }
+ if(p_info->act_area >= id->nr_areas)
+ return 1;
+ }
+out:
+ return 0;
+}
+
+/*
+ * debug_output:
+ * - called for user read()
+ * - copies formated debug entries to the user buffer
+ */
+
+static ssize_t
+debug_output(struct file *file, /* file descriptor */
+ char __user *user_buf, /* user buffer */
+ size_t len, /* length of buffer */
+ loff_t *offset) /* offset in the file */
+{
+ size_t count = 0;
+ size_t entry_offset;
+ file_private_info_t *p_info;
+
+ p_info = ((file_private_info_t *) file->private_data);
+ if (*offset != p_info->offset)
+ return -EPIPE;
+ if(p_info->act_area >= p_info->debug_info_snap->nr_areas)
+ return 0;
+ entry_offset = p_info->act_entry_offset;
+ while(count < len){
+ int formatted_line_size;
+ int formatted_line_residue;
+ int user_buf_residue;
+ size_t copy_size;
+
+ formatted_line_size = debug_format_entry(p_info);
+ formatted_line_residue = formatted_line_size - entry_offset;
+ user_buf_residue = len-count;
+ copy_size = min(user_buf_residue, formatted_line_residue);
+ if(copy_size){
+ if (copy_to_user(user_buf + count, p_info->temp_buf
+ + entry_offset, copy_size))
+ return -EFAULT;
+ count += copy_size;
+ entry_offset += copy_size;
+ }
+ if(copy_size == formatted_line_residue){
+ entry_offset = 0;
+ if(debug_next_entry(p_info))
+ goto out;
+ }
+ }
+out:
+ p_info->offset = *offset + count;
+ p_info->act_entry_offset = entry_offset;
+ *offset = p_info->offset;
+ return count;
+}
+
+/*
+ * debug_input:
+ * - called for user write()
+ * - calls input function of view
+ */
+
+static ssize_t
+debug_input(struct file *file, const char __user *user_buf, size_t length,
+ loff_t *offset)
+{
+ int rc = 0;
+ file_private_info_t *p_info;
+
+ mutex_lock(&debug_mutex);
+ p_info = ((file_private_info_t *) file->private_data);
+ if (p_info->view->input_proc)
+ rc = p_info->view->input_proc(p_info->debug_info_org,
+ p_info->view, file, user_buf,
+ length, offset);
+ else
+ rc = -EPERM;
+ mutex_unlock(&debug_mutex);
+ return rc; /* number of input characters */
+}
+
+/*
+ * debug_open:
+ * - called for user open()
+ * - copies formated output to private_data area of the file
+ * handle
+ */
+
+static int
+debug_open(struct inode *inode, struct file *file)
+{
+ int i, rc = 0;
+ file_private_info_t *p_info;
+ debug_info_t *debug_info, *debug_info_snapshot;
+
+ mutex_lock(&debug_mutex);
+ debug_info = file->f_path.dentry->d_inode->i_private;
+ /* find debug view */
+ for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
+ if (!debug_info->views[i])
+ continue;
+ else if (debug_info->debugfs_entries[i] ==
+ file->f_path.dentry) {
+ goto found; /* found view ! */
+ }
+ }
+ /* no entry found */
+ rc = -EINVAL;
+ goto out;
+
+found:
+
+ /* Make snapshot of current debug areas to get it consistent. */
+ /* To copy all the areas is only needed, if we have a view which */
+ /* formats the debug areas. */
+
+ if(!debug_info->views[i]->format_proc &&
+ !debug_info->views[i]->header_proc){
+ debug_info_snapshot = debug_info_copy(debug_info, NO_AREAS);
+ } else {
+ debug_info_snapshot = debug_info_copy(debug_info, ALL_AREAS);
+ }
+
+ if(!debug_info_snapshot){
+ rc = -ENOMEM;
+ goto out;
+ }
+ p_info = kmalloc(sizeof(file_private_info_t),
+ GFP_KERNEL);
+ if(!p_info){
+ debug_info_free(debug_info_snapshot);
+ rc = -ENOMEM;
+ goto out;
+ }
+ p_info->offset = 0;
+ p_info->debug_info_snap = debug_info_snapshot;
+ p_info->debug_info_org = debug_info;
+ p_info->view = debug_info->views[i];
+ p_info->act_area = 0;
+ p_info->act_page = 0;
+ p_info->act_entry = DEBUG_PROLOG_ENTRY;
+ p_info->act_entry_offset = 0;
+ file->private_data = p_info;
+ debug_info_get(debug_info);
+ nonseekable_open(inode, file);
+out:
+ mutex_unlock(&debug_mutex);
+ return rc;
+}
+
+/*
+ * debug_close:
+ * - called for user close()
+ * - deletes private_data area of the file handle
+ */
+
+static int
+debug_close(struct inode *inode, struct file *file)
+{
+ file_private_info_t *p_info;
+ p_info = (file_private_info_t *) file->private_data;
+ if(p_info->debug_info_snap)
+ debug_info_free(p_info->debug_info_snap);
+ debug_info_put(p_info->debug_info_org);
+ kfree(file->private_data);
+ return 0; /* success */
+}
+
+/*
+ * debug_register_mode:
+ * - Creates and initializes debug area for the caller
+ * The mode parameter allows to specify access rights for the s390dbf files
+ * - Returns handle for debug area
+ */
+
+debug_info_t *debug_register_mode(const char *name, int pages_per_area,
+ int nr_areas, int buf_size, umode_t mode,
+ uid_t uid, gid_t gid)
+{
+ debug_info_t *rc = NULL;
+
+ /* Since debugfs currently does not support uid/gid other than root, */
+ /* we do not allow gid/uid != 0 until we get support for that. */
+ if ((uid != 0) || (gid != 0))
+ pr_warning("Root becomes the owner of all s390dbf files "
+ "in sysfs\n");
+ BUG_ON(!initialized);
+ mutex_lock(&debug_mutex);
+
+ /* create new debug_info */
+
+ rc = debug_info_create(name, pages_per_area, nr_areas, buf_size, mode);
+ if(!rc)
+ goto out;
+ debug_register_view(rc, &debug_level_view);
+ debug_register_view(rc, &debug_flush_view);
+ debug_register_view(rc, &debug_pages_view);
+out:
+ if (!rc){
+ pr_err("Registering debug feature %s failed\n", name);
+ }
+ mutex_unlock(&debug_mutex);
+ return rc;
+}
+EXPORT_SYMBOL(debug_register_mode);
+
+/*
+ * debug_register:
+ * - creates and initializes debug area for the caller
+ * - returns handle for debug area
+ */
+
+debug_info_t *debug_register(const char *name, int pages_per_area,
+ int nr_areas, int buf_size)
+{
+ return debug_register_mode(name, pages_per_area, nr_areas, buf_size,
+ S_IRUSR | S_IWUSR, 0, 0);
+}
+
+/*
+ * debug_unregister:
+ * - give back debug area
+ */
+
+void
+debug_unregister(debug_info_t * id)
+{
+ if (!id)
+ goto out;
+ mutex_lock(&debug_mutex);
+ debug_info_put(id);
+ mutex_unlock(&debug_mutex);
+
+out:
+ return;
+}
+
+/*
+ * debug_set_size:
+ * - set area size (number of pages) and number of areas
+ */
+static int
+debug_set_size(debug_info_t* id, int nr_areas, int pages_per_area)
+{
+ unsigned long flags;
+ debug_entry_t *** new_areas;
+ int rc=0;
+
+ if(!id || (nr_areas <= 0) || (pages_per_area < 0))
+ return -EINVAL;
+ if(pages_per_area > 0){
+ new_areas = debug_areas_alloc(pages_per_area, nr_areas);
+ if(!new_areas) {
+ pr_info("Allocating memory for %i pages failed\n",
+ pages_per_area);
+ rc = -ENOMEM;
+ goto out;
+ }
+ } else {
+ new_areas = NULL;
+ }
+ spin_lock_irqsave(&id->lock,flags);
+ debug_areas_free(id);
+ id->areas = new_areas;
+ id->nr_areas = nr_areas;
+ id->pages_per_area = pages_per_area;
+ id->active_area = 0;
+ memset(id->active_entries,0,sizeof(int)*id->nr_areas);
+ memset(id->active_pages, 0, sizeof(int)*id->nr_areas);
+ spin_unlock_irqrestore(&id->lock,flags);
+ pr_info("%s: set new size (%i pages)\n" ,id->name, pages_per_area);
+out:
+ return rc;
+}
+
+/*
+ * debug_set_level:
+ * - set actual debug level
+ */
+
+void
+debug_set_level(debug_info_t* id, int new_level)
+{
+ unsigned long flags;
+ if(!id)
+ return;
+ spin_lock_irqsave(&id->lock,flags);
+ if(new_level == DEBUG_OFF_LEVEL){
+ id->level = DEBUG_OFF_LEVEL;
+ pr_info("%s: switched off\n",id->name);
+ } else if ((new_level > DEBUG_MAX_LEVEL) || (new_level < 0)) {
+ pr_info("%s: level %i is out of range (%i - %i)\n",
+ id->name, new_level, 0, DEBUG_MAX_LEVEL);
+ } else {
+ id->level = new_level;
+ }
+ spin_unlock_irqrestore(&id->lock,flags);
+}
+
+
+/*
+ * proceed_active_entry:
+ * - set active entry to next in the ring buffer
+ */
+
+static inline void
+proceed_active_entry(debug_info_t * id)
+{
+ if ((id->active_entries[id->active_area] += id->entry_size)
+ > (PAGE_SIZE - id->entry_size)){
+ id->active_entries[id->active_area] = 0;
+ id->active_pages[id->active_area] =
+ (id->active_pages[id->active_area] + 1) %
+ id->pages_per_area;
+ }
+}
+
+/*
+ * proceed_active_area:
+ * - set active area to next in the ring buffer
+ */
+
+static inline void
+proceed_active_area(debug_info_t * id)
+{
+ id->active_area++;
+ id->active_area = id->active_area % id->nr_areas;
+}
+
+/*
+ * get_active_entry:
+ */
+
+static inline debug_entry_t*
+get_active_entry(debug_info_t * id)
+{
+ return (debug_entry_t *) (((char *) id->areas[id->active_area]
+ [id->active_pages[id->active_area]]) +
+ id->active_entries[id->active_area]);
+}
+
+/*
+ * debug_finish_entry:
+ * - set timestamp, caller address, cpu number etc.
+ */
+
+static inline void
+debug_finish_entry(debug_info_t * id, debug_entry_t* active, int level,
+ int exception)
+{
+ active->id.stck = get_clock();
+ active->id.fields.cpuid = smp_processor_id();
+ active->caller = __builtin_return_address(0);
+ active->id.fields.exception = exception;
+ active->id.fields.level = level;
+ proceed_active_entry(id);
+ if(exception)
+ proceed_active_area(id);
+}
+
+static int debug_stoppable=1;
+static int debug_active=1;
+
+#define CTL_S390DBF_STOPPABLE 5678
+#define CTL_S390DBF_ACTIVE 5679
+
+/*
+ * proc handler for the running debug_active sysctl
+ * always allow read, allow write only if debug_stoppable is set or
+ * if debug_active is already off
+ */
+static int
+s390dbf_procactive(ctl_table *table, int write,
+ void __user *buffer, size_t *lenp, loff_t *ppos)
+{
+ if (!write || debug_stoppable || !debug_active)
+ return proc_dointvec(table, write, buffer, lenp, ppos);
+ else
+ return 0;
+}
+
+
+static struct ctl_table s390dbf_table[] = {
+ {
+ .procname = "debug_stoppable",
+ .data = &debug_stoppable,
+ .maxlen = sizeof(int),
+ .mode = S_IRUGO | S_IWUSR,
+ .proc_handler = proc_dointvec,
+ },
+ {
+ .procname = "debug_active",
+ .data = &debug_active,
+ .maxlen = sizeof(int),
+ .mode = S_IRUGO | S_IWUSR,
+ .proc_handler = s390dbf_procactive,
+ },
+ { }
+};
+
+static struct ctl_table s390dbf_dir_table[] = {
+ {
+ .procname = "s390dbf",
+ .maxlen = 0,
+ .mode = S_IRUGO | S_IXUGO,
+ .child = s390dbf_table,
+ },
+ { }
+};
+
+static struct ctl_table_header *s390dbf_sysctl_header;
+
+void
+debug_stop_all(void)
+{
+ if (debug_stoppable)
+ debug_active = 0;
+}
+
+
+void debug_set_critical(void)
+{
+ debug_critical = 1;
+}
+
+/*
+ * debug_event_common:
+ * - write debug entry with given size
+ */
+
+debug_entry_t*
+debug_event_common(debug_info_t * id, int level, const void *buf, int len)
+{
+ unsigned long flags;
+ debug_entry_t *active;
+
+ if (!debug_active || !id->areas)
+ return NULL;
+ if (debug_critical) {
+ if (!spin_trylock_irqsave(&id->lock, flags))
+ return NULL;
+ } else
+ spin_lock_irqsave(&id->lock, flags);
+ active = get_active_entry(id);
+ memset(DEBUG_DATA(active), 0, id->buf_size);
+ memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size));
+ debug_finish_entry(id, active, level, 0);
+ spin_unlock_irqrestore(&id->lock, flags);
+
+ return active;
+}
+
+/*
+ * debug_exception_common:
+ * - write debug entry with given size and switch to next debug area
+ */
+
+debug_entry_t
+*debug_exception_common(debug_info_t * id, int level, const void *buf, int len)
+{
+ unsigned long flags;
+ debug_entry_t *active;
+
+ if (!debug_active || !id->areas)
+ return NULL;
+ if (debug_critical) {
+ if (!spin_trylock_irqsave(&id->lock, flags))
+ return NULL;
+ } else
+ spin_lock_irqsave(&id->lock, flags);
+ active = get_active_entry(id);
+ memset(DEBUG_DATA(active), 0, id->buf_size);
+ memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size));
+ debug_finish_entry(id, active, level, 1);
+ spin_unlock_irqrestore(&id->lock, flags);
+
+ return active;
+}
+
+/*
+ * counts arguments in format string for sprintf view
+ */
+
+static inline int
+debug_count_numargs(char *string)
+{
+ int numargs=0;
+
+ while(*string) {
+ if(*string++=='%')
+ numargs++;
+ }
+ return(numargs);
+}
+
+/*
+ * debug_sprintf_event:
+ */
+
+debug_entry_t*
+debug_sprintf_event(debug_info_t* id, int level,char *string,...)
+{
+ va_list ap;
+ int numargs,idx;
+ unsigned long flags;
+ debug_sprintf_entry_t *curr_event;
+ debug_entry_t *active;
+
+ if((!id) || (level > id->level))
+ return NULL;
+ if (!debug_active || !id->areas)
+ return NULL;
+ numargs=debug_count_numargs(string);
+
+ if (debug_critical) {
+ if (!spin_trylock_irqsave(&id->lock, flags))
+ return NULL;
+ } else
+ spin_lock_irqsave(&id->lock, flags);
+ active = get_active_entry(id);
+ curr_event=(debug_sprintf_entry_t *) DEBUG_DATA(active);
+ va_start(ap,string);
+ curr_event->string=string;
+ for(idx=0;idx<min(numargs,(int)(id->buf_size / sizeof(long))-1);idx++)
+ curr_event->args[idx]=va_arg(ap,long);
+ va_end(ap);
+ debug_finish_entry(id, active, level, 0);
+ spin_unlock_irqrestore(&id->lock, flags);
+
+ return active;
+}
+
+/*
+ * debug_sprintf_exception:
+ */
+
+debug_entry_t*
+debug_sprintf_exception(debug_info_t* id, int level,char *string,...)
+{
+ va_list ap;
+ int numargs,idx;
+ unsigned long flags;
+ debug_sprintf_entry_t *curr_event;
+ debug_entry_t *active;
+
+ if((!id) || (level > id->level))
+ return NULL;
+ if (!debug_active || !id->areas)
+ return NULL;
+
+ numargs=debug_count_numargs(string);
+
+ if (debug_critical) {
+ if (!spin_trylock_irqsave(&id->lock, flags))
+ return NULL;
+ } else
+ spin_lock_irqsave(&id->lock, flags);
+ active = get_active_entry(id);
+ curr_event=(debug_sprintf_entry_t *)DEBUG_DATA(active);
+ va_start(ap,string);
+ curr_event->string=string;
+ for(idx=0;idx<min(numargs,(int)(id->buf_size / sizeof(long))-1);idx++)
+ curr_event->args[idx]=va_arg(ap,long);
+ va_end(ap);
+ debug_finish_entry(id, active, level, 1);
+ spin_unlock_irqrestore(&id->lock, flags);
+
+ return active;
+}
+
+/*
+ * debug_init:
+ * - is called exactly once to initialize the debug feature
+ */
+
+static int
+__init debug_init(void)
+{
+ int rc = 0;
+
+ s390dbf_sysctl_header = register_sysctl_table(s390dbf_dir_table);
+ mutex_lock(&debug_mutex);
+ debug_debugfs_root_entry = debugfs_create_dir(DEBUG_DIR_ROOT,NULL);
+ initialized = 1;
+ mutex_unlock(&debug_mutex);
+
+ return rc;
+}
+
+/*
+ * debug_register_view:
+ */
+
+int
+debug_register_view(debug_info_t * id, struct debug_view *view)
+{
+ int rc = 0;
+ int i;
+ unsigned long flags;
+ umode_t mode;
+ struct dentry *pde;
+
+ if (!id)
+ goto out;
+ mode = (id->mode | S_IFREG) & ~S_IXUGO;
+ if (!(view->prolog_proc || view->format_proc || view->header_proc))
+ mode &= ~(S_IRUSR | S_IRGRP | S_IROTH);
+ if (!view->input_proc)
+ mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ pde = debugfs_create_file(view->name, mode, id->debugfs_root_entry,
+ id , &debug_file_ops);
+ if (!pde){
+ pr_err("Registering view %s/%s failed due to out of "
+ "memory\n", id->name,view->name);
+ rc = -1;
+ goto out;
+ }
+ spin_lock_irqsave(&id->lock, flags);
+ for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
+ if (!id->views[i])
+ break;
+ }
+ if (i == DEBUG_MAX_VIEWS) {
+ pr_err("Registering view %s/%s would exceed the maximum "
+ "number of views %i\n", id->name, view->name, i);
+ debugfs_remove(pde);
+ rc = -1;
+ } else {
+ id->views[i] = view;
+ id->debugfs_entries[i] = pde;
+ }
+ spin_unlock_irqrestore(&id->lock, flags);
+out:
+ return rc;
+}
+
+/*
+ * debug_unregister_view:
+ */
+
+int
+debug_unregister_view(debug_info_t * id, struct debug_view *view)
+{
+ int rc = 0;
+ int i;
+ unsigned long flags;
+
+ if (!id)
+ goto out;
+ spin_lock_irqsave(&id->lock, flags);
+ for (i = 0; i < DEBUG_MAX_VIEWS; i++) {
+ if (id->views[i] == view)
+ break;
+ }
+ if (i == DEBUG_MAX_VIEWS)
+ rc = -1;
+ else {
+ debugfs_remove(id->debugfs_entries[i]);
+ id->views[i] = NULL;
+ }
+ spin_unlock_irqrestore(&id->lock, flags);
+out:
+ return rc;
+}
+
+static inline char *
+debug_get_user_string(const char __user *user_buf, size_t user_len)
+{
+ char* buffer;
+
+ buffer = kmalloc(user_len + 1, GFP_KERNEL);
+ if (!buffer)
+ return ERR_PTR(-ENOMEM);
+ if (copy_from_user(buffer, user_buf, user_len) != 0) {
+ kfree(buffer);
+ return ERR_PTR(-EFAULT);
+ }
+ /* got the string, now strip linefeed. */
+ if (buffer[user_len - 1] == '\n')
+ buffer[user_len - 1] = 0;
+ else
+ buffer[user_len] = 0;
+ return buffer;
+}
+
+static inline int
+debug_get_uint(char *buf)
+{
+ int rc;
+
+ buf = skip_spaces(buf);
+ rc = simple_strtoul(buf, &buf, 10);
+ if(*buf){
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+/*
+ * functions for debug-views
+ ***********************************
+*/
+
+/*
+ * prints out actual debug level
+ */
+
+static int
+debug_prolog_pages_fn(debug_info_t * id,
+ struct debug_view *view, char *out_buf)
+{
+ return sprintf(out_buf, "%i\n", id->pages_per_area);
+}
+
+/*
+ * reads new size (number of pages per debug area)
+ */
+
+static int
+debug_input_pages_fn(debug_info_t * id, struct debug_view *view,
+ struct file *file, const char __user *user_buf,
+ size_t user_len, loff_t * offset)
+{
+ char *str;
+ int rc,new_pages;
+
+ if (user_len > 0x10000)
+ user_len = 0x10000;
+ if (*offset != 0){
+ rc = -EPIPE;
+ goto out;
+ }
+ str = debug_get_user_string(user_buf,user_len);
+ if(IS_ERR(str)){
+ rc = PTR_ERR(str);
+ goto out;
+ }
+ new_pages = debug_get_uint(str);
+ if(new_pages < 0){
+ rc = -EINVAL;
+ goto free_str;
+ }
+ rc = debug_set_size(id,id->nr_areas, new_pages);
+ if(rc != 0){
+ rc = -EINVAL;
+ goto free_str;
+ }
+ rc = user_len;
+free_str:
+ kfree(str);
+out:
+ *offset += user_len;
+ return rc; /* number of input characters */
+}
+
+/*
+ * prints out actual debug level
+ */
+
+static int
+debug_prolog_level_fn(debug_info_t * id, struct debug_view *view, char *out_buf)
+{
+ int rc = 0;
+
+ if(id->level == DEBUG_OFF_LEVEL) {
+ rc = sprintf(out_buf,"-\n");
+ }
+ else {
+ rc = sprintf(out_buf, "%i\n", id->level);
+ }
+ return rc;
+}
+
+/*
+ * reads new debug level
+ */
+
+static int
+debug_input_level_fn(debug_info_t * id, struct debug_view *view,
+ struct file *file, const char __user *user_buf,
+ size_t user_len, loff_t * offset)
+{
+ char *str;
+ int rc,new_level;
+
+ if (user_len > 0x10000)
+ user_len = 0x10000;
+ if (*offset != 0){
+ rc = -EPIPE;
+ goto out;
+ }
+ str = debug_get_user_string(user_buf,user_len);
+ if(IS_ERR(str)){
+ rc = PTR_ERR(str);
+ goto out;
+ }
+ if(str[0] == '-'){
+ debug_set_level(id, DEBUG_OFF_LEVEL);
+ rc = user_len;
+ goto free_str;
+ } else {
+ new_level = debug_get_uint(str);
+ }
+ if(new_level < 0) {
+ pr_warning("%s is not a valid level for a debug "
+ "feature\n", str);
+ rc = -EINVAL;
+ } else {
+ debug_set_level(id, new_level);
+ rc = user_len;
+ }
+free_str:
+ kfree(str);
+out:
+ *offset += user_len;
+ return rc; /* number of input characters */
+}
+
+
+/*
+ * flushes debug areas
+ */
+
+static void debug_flush(debug_info_t* id, int area)
+{
+ unsigned long flags;
+ int i,j;
+
+ if(!id || !id->areas)
+ return;
+ spin_lock_irqsave(&id->lock,flags);
+ if(area == DEBUG_FLUSH_ALL){
+ id->active_area = 0;
+ memset(id->active_entries, 0, id->nr_areas * sizeof(int));
+ for (i = 0; i < id->nr_areas; i++) {
+ id->active_pages[i] = 0;
+ for(j = 0; j < id->pages_per_area; j++) {
+ memset(id->areas[i][j], 0, PAGE_SIZE);
+ }
+ }
+ } else if(area >= 0 && area < id->nr_areas) {
+ id->active_entries[area] = 0;
+ id->active_pages[area] = 0;
+ for(i = 0; i < id->pages_per_area; i++) {
+ memset(id->areas[area][i],0,PAGE_SIZE);
+ }
+ }
+ spin_unlock_irqrestore(&id->lock,flags);
+}
+
+/*
+ * view function: flushes debug areas
+ */
+
+static int
+debug_input_flush_fn(debug_info_t * id, struct debug_view *view,
+ struct file *file, const char __user *user_buf,
+ size_t user_len, loff_t * offset)
+{
+ char input_buf[1];
+ int rc = user_len;
+
+ if (user_len > 0x10000)
+ user_len = 0x10000;
+ if (*offset != 0){
+ rc = -EPIPE;
+ goto out;
+ }
+ if (copy_from_user(input_buf, user_buf, 1)){
+ rc = -EFAULT;
+ goto out;
+ }
+ if(input_buf[0] == '-') {
+ debug_flush(id, DEBUG_FLUSH_ALL);
+ goto out;
+ }
+ if (isdigit(input_buf[0])) {
+ int area = ((int) input_buf[0] - (int) '0');
+ debug_flush(id, area);
+ goto out;
+ }
+
+ pr_info("Flushing debug data failed because %c is not a valid "
+ "area\n", input_buf[0]);
+
+out:
+ *offset += user_len;
+ return rc; /* number of input characters */
+}
+
+/*
+ * prints debug header in raw format
+ */
+
+static int
+debug_raw_header_fn(debug_info_t * id, struct debug_view *view,
+ int area, debug_entry_t * entry, char *out_buf)
+{
+ int rc;
+
+ rc = sizeof(debug_entry_t);
+ memcpy(out_buf,entry,sizeof(debug_entry_t));
+ return rc;
+}
+
+/*
+ * prints debug data in raw format
+ */
+
+static int
+debug_raw_format_fn(debug_info_t * id, struct debug_view *view,
+ char *out_buf, const char *in_buf)
+{
+ int rc;
+
+ rc = id->buf_size;
+ memcpy(out_buf, in_buf, id->buf_size);
+ return rc;
+}
+
+/*
+ * prints debug data in hex/ascii format
+ */
+
+static int
+debug_hex_ascii_format_fn(debug_info_t * id, struct debug_view *view,
+ char *out_buf, const char *in_buf)
+{
+ int i, rc = 0;
+
+ for (i = 0; i < id->buf_size; i++) {
+ rc += sprintf(out_buf + rc, "%02x ",
+ ((unsigned char *) in_buf)[i]);
+ }
+ rc += sprintf(out_buf + rc, "| ");
+ for (i = 0; i < id->buf_size; i++) {
+ unsigned char c = in_buf[i];
+ if (isascii(c) && isprint(c))
+ rc += sprintf(out_buf + rc, "%c", c);
+ else
+ rc += sprintf(out_buf + rc, ".");
+ }
+ rc += sprintf(out_buf + rc, "\n");
+ return rc;
+}
+
+/*
+ * prints header for debug entry
+ */
+
+int
+debug_dflt_header_fn(debug_info_t * id, struct debug_view *view,
+ int area, debug_entry_t * entry, char *out_buf)
+{
+ struct timespec time_spec;
+ char *except_str;
+ unsigned long caller;
+ int rc = 0;
+ unsigned int level;
+
+ level = entry->id.fields.level;
+ stck_to_timespec(entry->id.stck, &time_spec);
+
+ if (entry->id.fields.exception)
+ except_str = "*";
+ else
+ except_str = "-";
+ caller = ((unsigned long) entry->caller) & PSW_ADDR_INSN;
+ rc += sprintf(out_buf, "%02i %011lu:%06lu %1u %1s %02i %p ",
+ area, time_spec.tv_sec, time_spec.tv_nsec / 1000, level,
+ except_str, entry->id.fields.cpuid, (void *) caller);
+ return rc;
+}
+
+/*
+ * prints debug data sprintf-formated:
+ * debug_sprinf_event/exception calls must be used together with this view
+ */
+
+#define DEBUG_SPRINTF_MAX_ARGS 10
+
+static int
+debug_sprintf_format_fn(debug_info_t * id, struct debug_view *view,
+ char *out_buf, debug_sprintf_entry_t *curr_event)
+{
+ int num_longs, num_used_args = 0,i, rc = 0;
+ int index[DEBUG_SPRINTF_MAX_ARGS];
+
+ /* count of longs fit into one entry */
+ num_longs = id->buf_size / sizeof(long);
+
+ if(num_longs < 1)
+ goto out; /* bufsize of entry too small */
+ if(num_longs == 1) {
+ /* no args, we use only the string */
+ strcpy(out_buf, curr_event->string);
+ rc = strlen(curr_event->string);
+ goto out;
+ }
+
+ /* number of arguments used for sprintf (without the format string) */
+ num_used_args = min(DEBUG_SPRINTF_MAX_ARGS, (num_longs - 1));
+
+ memset(index,0, DEBUG_SPRINTF_MAX_ARGS * sizeof(int));
+
+ for(i = 0; i < num_used_args; i++)
+ index[i] = i;
+
+ rc = sprintf(out_buf, curr_event->string, curr_event->args[index[0]],
+ curr_event->args[index[1]], curr_event->args[index[2]],
+ curr_event->args[index[3]], curr_event->args[index[4]],
+ curr_event->args[index[5]], curr_event->args[index[6]],
+ curr_event->args[index[7]], curr_event->args[index[8]],
+ curr_event->args[index[9]]);
+
+out:
+
+ return rc;
+}
+
+/*
+ * clean up module
+ */
+static void __exit debug_exit(void)
+{
+ debugfs_remove(debug_debugfs_root_entry);
+ unregister_sysctl_table(s390dbf_sysctl_header);
+ return;
+}
+
+/*
+ * module definitions
+ */
+postcore_initcall(debug_init);
+module_exit(debug_exit);
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(debug_register);
+EXPORT_SYMBOL(debug_unregister);
+EXPORT_SYMBOL(debug_set_level);
+EXPORT_SYMBOL(debug_stop_all);
+EXPORT_SYMBOL(debug_register_view);
+EXPORT_SYMBOL(debug_unregister_view);
+EXPORT_SYMBOL(debug_event_common);
+EXPORT_SYMBOL(debug_exception_common);
+EXPORT_SYMBOL(debug_hex_ascii_view);
+EXPORT_SYMBOL(debug_raw_view);
+EXPORT_SYMBOL(debug_dflt_header_fn);
+EXPORT_SYMBOL(debug_sprintf_view);
+EXPORT_SYMBOL(debug_sprintf_exception);
+EXPORT_SYMBOL(debug_sprintf_event);
diff --git a/arch/s390/kernel/diag.c b/arch/s390/kernel/diag.c
new file mode 100644
index 00000000..8237fc07
--- /dev/null
+++ b/arch/s390/kernel/diag.c
@@ -0,0 +1,81 @@
+/*
+ * Implementation of s390 diagnose codes
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Michael Holzheu <holzheu@de.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <asm/diag.h>
+
+/*
+ * Diagnose 14: Input spool file manipulation
+ */
+int diag14(unsigned long rx, unsigned long ry1, unsigned long subcode)
+{
+ register unsigned long _ry1 asm("2") = ry1;
+ register unsigned long _ry2 asm("3") = subcode;
+ int rc = 0;
+
+ asm volatile(
+#ifdef CONFIG_64BIT
+ " sam31\n"
+ " diag %2,2,0x14\n"
+ " sam64\n"
+#else
+ " diag %2,2,0x14\n"
+#endif
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (rc), "+d" (_ry2)
+ : "d" (rx), "d" (_ry1)
+ : "cc");
+
+ return rc;
+}
+EXPORT_SYMBOL(diag14);
+
+/*
+ * Diagnose 210: Get information about a virtual device
+ */
+int diag210(struct diag210 *addr)
+{
+ /*
+ * diag 210 needs its data below the 2GB border, so we
+ * use a static data area to be sure
+ */
+ static struct diag210 diag210_tmp;
+ static DEFINE_SPINLOCK(diag210_lock);
+ unsigned long flags;
+ int ccode;
+
+ spin_lock_irqsave(&diag210_lock, flags);
+ diag210_tmp = *addr;
+
+#ifdef CONFIG_64BIT
+ asm volatile(
+ " lhi %0,-1\n"
+ " sam31\n"
+ " diag %1,0,0x210\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1: sam64\n"
+ EX_TABLE(0b, 1b)
+ : "=&d" (ccode) : "a" (&diag210_tmp) : "cc", "memory");
+#else
+ asm volatile(
+ " lhi %0,-1\n"
+ " diag %1,0,0x210\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "=&d" (ccode) : "a" (&diag210_tmp) : "cc", "memory");
+#endif
+
+ *addr = diag210_tmp;
+ spin_unlock_irqrestore(&diag210_lock, flags);
+
+ return ccode;
+}
+EXPORT_SYMBOL(diag210);
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c
new file mode 100644
index 00000000..3221c6fc
--- /dev/null
+++ b/arch/s390/kernel/dis.c
@@ -0,0 +1,1604 @@
+/*
+ * arch/s390/kernel/dis.c
+ *
+ * Disassemble s390 instructions.
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ */
+
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/kallsyms.h>
+#include <linux/reboot.h>
+#include <linux/kprobes.h>
+#include <linux/kdebug.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/atomic.h>
+#include <asm/mathemu.h>
+#include <asm/cpcmd.h>
+#include <asm/lowcore.h>
+#include <asm/debug.h>
+#include <asm/irq.h>
+
+#ifndef CONFIG_64BIT
+#define ONELONG "%08lx: "
+#else /* CONFIG_64BIT */
+#define ONELONG "%016lx: "
+#endif /* CONFIG_64BIT */
+
+#define OPERAND_GPR 0x1 /* Operand printed as %rx */
+#define OPERAND_FPR 0x2 /* Operand printed as %fx */
+#define OPERAND_AR 0x4 /* Operand printed as %ax */
+#define OPERAND_CR 0x8 /* Operand printed as %cx */
+#define OPERAND_DISP 0x10 /* Operand printed as displacement */
+#define OPERAND_BASE 0x20 /* Operand printed as base register */
+#define OPERAND_INDEX 0x40 /* Operand printed as index register */
+#define OPERAND_PCREL 0x80 /* Operand printed as pc-relative symbol */
+#define OPERAND_SIGNED 0x100 /* Operand printed as signed value */
+#define OPERAND_LENGTH 0x200 /* Operand printed as length (+1) */
+
+enum {
+ UNUSED, /* Indicates the end of the operand list */
+ R_8, /* GPR starting at position 8 */
+ R_12, /* GPR starting at position 12 */
+ R_16, /* GPR starting at position 16 */
+ R_20, /* GPR starting at position 20 */
+ R_24, /* GPR starting at position 24 */
+ R_28, /* GPR starting at position 28 */
+ R_32, /* GPR starting at position 32 */
+ F_8, /* FPR starting at position 8 */
+ F_12, /* FPR starting at position 12 */
+ F_16, /* FPR starting at position 16 */
+ F_20, /* FPR starting at position 16 */
+ F_24, /* FPR starting at position 24 */
+ F_28, /* FPR starting at position 28 */
+ F_32, /* FPR starting at position 32 */
+ A_8, /* Access reg. starting at position 8 */
+ A_12, /* Access reg. starting at position 12 */
+ A_24, /* Access reg. starting at position 24 */
+ A_28, /* Access reg. starting at position 28 */
+ C_8, /* Control reg. starting at position 8 */
+ C_12, /* Control reg. starting at position 12 */
+ B_16, /* Base register starting at position 16 */
+ B_32, /* Base register starting at position 32 */
+ X_12, /* Index register starting at position 12 */
+ D_20, /* Displacement starting at position 20 */
+ D_36, /* Displacement starting at position 36 */
+ D20_20, /* 20 bit displacement starting at 20 */
+ L4_8, /* 4 bit length starting at position 8 */
+ L4_12, /* 4 bit length starting at position 12 */
+ L8_8, /* 8 bit length starting at position 8 */
+ U4_8, /* 4 bit unsigned value starting at 8 */
+ U4_12, /* 4 bit unsigned value starting at 12 */
+ U4_16, /* 4 bit unsigned value starting at 16 */
+ U4_20, /* 4 bit unsigned value starting at 20 */
+ U4_32, /* 4 bit unsigned value starting at 32 */
+ U8_8, /* 8 bit unsigned value starting at 8 */
+ U8_16, /* 8 bit unsigned value starting at 16 */
+ U8_24, /* 8 bit unsigned value starting at 24 */
+ U8_32, /* 8 bit unsigned value starting at 32 */
+ I8_8, /* 8 bit signed value starting at 8 */
+ I8_32, /* 8 bit signed value starting at 32 */
+ I16_16, /* 16 bit signed value starting at 16 */
+ I16_32, /* 32 bit signed value starting at 16 */
+ U16_16, /* 16 bit unsigned value starting at 16 */
+ U16_32, /* 32 bit unsigned value starting at 16 */
+ J16_16, /* PC relative jump offset at 16 */
+ J32_16, /* PC relative long offset at 16 */
+ I32_16, /* 32 bit signed value starting at 16 */
+ U32_16, /* 32 bit unsigned value starting at 16 */
+ M_16, /* 4 bit optional mask starting at 16 */
+ RO_28, /* optional GPR starting at position 28 */
+};
+
+/*
+ * Enumeration of the different instruction formats.
+ * For details consult the principles of operation.
+ */
+enum {
+ INSTR_INVALID,
+ INSTR_E,
+ INSTR_RIE_R0IU, INSTR_RIE_R0UU, INSTR_RIE_RRP, INSTR_RIE_RRPU,
+ INSTR_RIE_RRUUU, INSTR_RIE_RUPI, INSTR_RIE_RUPU, INSTR_RIE_RRI0,
+ INSTR_RIL_RI, INSTR_RIL_RP, INSTR_RIL_RU, INSTR_RIL_UP,
+ INSTR_RIS_R0RDU, INSTR_RIS_R0UU, INSTR_RIS_RURDI, INSTR_RIS_RURDU,
+ INSTR_RI_RI, INSTR_RI_RP, INSTR_RI_RU, INSTR_RI_UP,
+ INSTR_RRE_00, INSTR_RRE_0R, INSTR_RRE_AA, INSTR_RRE_AR, INSTR_RRE_F0,
+ INSTR_RRE_FF, INSTR_RRE_FR, INSTR_RRE_R0, INSTR_RRE_RA, INSTR_RRE_RF,
+ INSTR_RRE_RR, INSTR_RRE_RR_OPT,
+ INSTR_RRF_0UFF, INSTR_RRF_F0FF, INSTR_RRF_F0FF2, INSTR_RRF_F0FR,
+ INSTR_RRF_FFRU, INSTR_RRF_FUFF, INSTR_RRF_M0RR, INSTR_RRF_R0RR,
+ INSTR_RRF_R0RR2, INSTR_RRF_RURR, INSTR_RRF_U0FF, INSTR_RRF_U0RF,
+ INSTR_RRF_U0RR, INSTR_RRF_UUFF, INSTR_RRR_F0FF, INSTR_RRS_RRRDU,
+ INSTR_RR_FF, INSTR_RR_R0, INSTR_RR_RR, INSTR_RR_U0, INSTR_RR_UR,
+ INSTR_RSE_CCRD, INSTR_RSE_RRRD, INSTR_RSE_RURD,
+ INSTR_RSI_RRP,
+ INSTR_RSL_R0RD,
+ INSTR_RSY_AARD, INSTR_RSY_CCRD, INSTR_RSY_RRRD, INSTR_RSY_RURD,
+ INSTR_RSY_RDRM,
+ INSTR_RS_AARD, INSTR_RS_CCRD, INSTR_RS_R0RD, INSTR_RS_RRRD,
+ INSTR_RS_RURD,
+ INSTR_RXE_FRRD, INSTR_RXE_RRRD,
+ INSTR_RXF_FRRDF,
+ INSTR_RXY_FRRD, INSTR_RXY_RRRD, INSTR_RXY_URRD,
+ INSTR_RX_FRRD, INSTR_RX_RRRD, INSTR_RX_URRD,
+ INSTR_SIL_RDI, INSTR_SIL_RDU,
+ INSTR_SIY_IRD, INSTR_SIY_URD,
+ INSTR_SI_URD,
+ INSTR_SSE_RDRD,
+ INSTR_SSF_RRDRD, INSTR_SSF_RRDRD2,
+ INSTR_SS_L0RDRD, INSTR_SS_LIRDRD, INSTR_SS_LLRDRD, INSTR_SS_RRRDRD,
+ INSTR_SS_RRRDRD2, INSTR_SS_RRRDRD3,
+ INSTR_S_00, INSTR_S_RD,
+};
+
+struct operand {
+ int bits; /* The number of bits in the operand. */
+ int shift; /* The number of bits to shift. */
+ int flags; /* One bit syntax flags. */
+};
+
+struct insn {
+ const char name[5];
+ unsigned char opfrag;
+ unsigned char format;
+};
+
+static const struct operand operands[] =
+{
+ [UNUSED] = { 0, 0, 0 },
+ [R_8] = { 4, 8, OPERAND_GPR },
+ [R_12] = { 4, 12, OPERAND_GPR },
+ [R_16] = { 4, 16, OPERAND_GPR },
+ [R_20] = { 4, 20, OPERAND_GPR },
+ [R_24] = { 4, 24, OPERAND_GPR },
+ [R_28] = { 4, 28, OPERAND_GPR },
+ [R_32] = { 4, 32, OPERAND_GPR },
+ [F_8] = { 4, 8, OPERAND_FPR },
+ [F_12] = { 4, 12, OPERAND_FPR },
+ [F_16] = { 4, 16, OPERAND_FPR },
+ [F_20] = { 4, 16, OPERAND_FPR },
+ [F_24] = { 4, 24, OPERAND_FPR },
+ [F_28] = { 4, 28, OPERAND_FPR },
+ [F_32] = { 4, 32, OPERAND_FPR },
+ [A_8] = { 4, 8, OPERAND_AR },
+ [A_12] = { 4, 12, OPERAND_AR },
+ [A_24] = { 4, 24, OPERAND_AR },
+ [A_28] = { 4, 28, OPERAND_AR },
+ [C_8] = { 4, 8, OPERAND_CR },
+ [C_12] = { 4, 12, OPERAND_CR },
+ [B_16] = { 4, 16, OPERAND_BASE | OPERAND_GPR },
+ [B_32] = { 4, 32, OPERAND_BASE | OPERAND_GPR },
+ [X_12] = { 4, 12, OPERAND_INDEX | OPERAND_GPR },
+ [D_20] = { 12, 20, OPERAND_DISP },
+ [D_36] = { 12, 36, OPERAND_DISP },
+ [D20_20] = { 20, 20, OPERAND_DISP | OPERAND_SIGNED },
+ [L4_8] = { 4, 8, OPERAND_LENGTH },
+ [L4_12] = { 4, 12, OPERAND_LENGTH },
+ [L8_8] = { 8, 8, OPERAND_LENGTH },
+ [U4_8] = { 4, 8, 0 },
+ [U4_12] = { 4, 12, 0 },
+ [U4_16] = { 4, 16, 0 },
+ [U4_20] = { 4, 20, 0 },
+ [U4_32] = { 4, 32, 0 },
+ [U8_8] = { 8, 8, 0 },
+ [U8_16] = { 8, 16, 0 },
+ [U8_24] = { 8, 24, 0 },
+ [U8_32] = { 8, 32, 0 },
+ [I16_16] = { 16, 16, OPERAND_SIGNED },
+ [U16_16] = { 16, 16, 0 },
+ [U16_32] = { 16, 32, 0 },
+ [J16_16] = { 16, 16, OPERAND_PCREL },
+ [I16_32] = { 16, 32, OPERAND_SIGNED },
+ [J32_16] = { 32, 16, OPERAND_PCREL },
+ [I32_16] = { 32, 16, OPERAND_SIGNED },
+ [U32_16] = { 32, 16, 0 },
+ [M_16] = { 4, 16, 0 },
+ [RO_28] = { 4, 28, OPERAND_GPR }
+};
+
+static const unsigned char formats[][7] = {
+ [INSTR_E] = { 0xff, 0,0,0,0,0,0 },
+ [INSTR_RIE_R0UU] = { 0xff, R_8,U16_16,U4_32,0,0,0 },
+ [INSTR_RIE_RRPU] = { 0xff, R_8,R_12,U4_32,J16_16,0,0 },
+ [INSTR_RIE_RRP] = { 0xff, R_8,R_12,J16_16,0,0,0 },
+ [INSTR_RIE_RRUUU] = { 0xff, R_8,R_12,U8_16,U8_24,U8_32,0 },
+ [INSTR_RIE_RUPI] = { 0xff, R_8,I8_32,U4_12,J16_16,0,0 },
+ [INSTR_RIE_RRI0] = { 0xff, R_8,R_12,I16_16,0,0,0 },
+ [INSTR_RIL_RI] = { 0x0f, R_8,I32_16,0,0,0,0 },
+ [INSTR_RIL_RP] = { 0x0f, R_8,J32_16,0,0,0,0 },
+ [INSTR_RIL_RU] = { 0x0f, R_8,U32_16,0,0,0,0 },
+ [INSTR_RIL_UP] = { 0x0f, U4_8,J32_16,0,0,0,0 },
+ [INSTR_RIS_R0RDU] = { 0xff, R_8,U8_32,D_20,B_16,0,0 },
+ [INSTR_RIS_RURDI] = { 0xff, R_8,I8_32,U4_12,D_20,B_16,0 },
+ [INSTR_RIS_RURDU] = { 0xff, R_8,U8_32,U4_12,D_20,B_16,0 },
+ [INSTR_RI_RI] = { 0x0f, R_8,I16_16,0,0,0,0 },
+ [INSTR_RI_RP] = { 0x0f, R_8,J16_16,0,0,0,0 },
+ [INSTR_RI_RU] = { 0x0f, R_8,U16_16,0,0,0,0 },
+ [INSTR_RI_UP] = { 0x0f, U4_8,J16_16,0,0,0,0 },
+ [INSTR_RRE_00] = { 0xff, 0,0,0,0,0,0 },
+ [INSTR_RRE_0R] = { 0xff, R_28,0,0,0,0,0 },
+ [INSTR_RRE_AA] = { 0xff, A_24,A_28,0,0,0,0 },
+ [INSTR_RRE_AR] = { 0xff, A_24,R_28,0,0,0,0 },
+ [INSTR_RRE_F0] = { 0xff, F_24,0,0,0,0,0 },
+ [INSTR_RRE_FF] = { 0xff, F_24,F_28,0,0,0,0 },
+ [INSTR_RRE_FR] = { 0xff, F_24,R_28,0,0,0,0 },
+ [INSTR_RRE_R0] = { 0xff, R_24,0,0,0,0,0 },
+ [INSTR_RRE_RA] = { 0xff, R_24,A_28,0,0,0,0 },
+ [INSTR_RRE_RF] = { 0xff, R_24,F_28,0,0,0,0 },
+ [INSTR_RRE_RR] = { 0xff, R_24,R_28,0,0,0,0 },
+ [INSTR_RRE_RR_OPT]= { 0xff, R_24,RO_28,0,0,0,0 },
+ [INSTR_RRF_0UFF] = { 0xff, F_24,F_28,U4_20,0,0,0 },
+ [INSTR_RRF_F0FF2] = { 0xff, F_24,F_16,F_28,0,0,0 },
+ [INSTR_RRF_F0FF] = { 0xff, F_16,F_24,F_28,0,0,0 },
+ [INSTR_RRF_F0FR] = { 0xff, F_24,F_16,R_28,0,0,0 },
+ [INSTR_RRF_FFRU] = { 0xff, F_24,F_16,R_28,U4_20,0,0 },
+ [INSTR_RRF_FUFF] = { 0xff, F_24,F_16,F_28,U4_20,0,0 },
+ [INSTR_RRF_M0RR] = { 0xff, R_24,R_28,M_16,0,0,0 },
+ [INSTR_RRF_R0RR] = { 0xff, R_24,R_16,R_28,0,0,0 },
+ [INSTR_RRF_R0RR2] = { 0xff, R_24,R_28,R_16,0,0,0 },
+ [INSTR_RRF_RURR] = { 0xff, R_24,R_28,R_16,U4_20,0,0 },
+ [INSTR_RRF_U0FF] = { 0xff, F_24,U4_16,F_28,0,0,0 },
+ [INSTR_RRF_U0RF] = { 0xff, R_24,U4_16,F_28,0,0,0 },
+ [INSTR_RRF_U0RR] = { 0xff, R_24,R_28,U4_16,0,0,0 },
+ [INSTR_RRF_UUFF] = { 0xff, F_24,U4_16,F_28,U4_20,0,0 },
+ [INSTR_RRR_F0FF] = { 0xff, F_24,F_28,F_16,0,0,0 },
+ [INSTR_RRS_RRRDU] = { 0xff, R_8,R_12,U4_32,D_20,B_16,0 },
+ [INSTR_RR_FF] = { 0xff, F_8,F_12,0,0,0,0 },
+ [INSTR_RR_R0] = { 0xff, R_8, 0,0,0,0,0 },
+ [INSTR_RR_RR] = { 0xff, R_8,R_12,0,0,0,0 },
+ [INSTR_RR_U0] = { 0xff, U8_8, 0,0,0,0,0 },
+ [INSTR_RR_UR] = { 0xff, U4_8,R_12,0,0,0,0 },
+ [INSTR_RSE_CCRD] = { 0xff, C_8,C_12,D_20,B_16,0,0 },
+ [INSTR_RSE_RRRD] = { 0xff, R_8,R_12,D_20,B_16,0,0 },
+ [INSTR_RSE_RURD] = { 0xff, R_8,U4_12,D_20,B_16,0,0 },
+ [INSTR_RSI_RRP] = { 0xff, R_8,R_12,J16_16,0,0,0 },
+ [INSTR_RSL_R0RD] = { 0xff, D_20,L4_8,B_16,0,0,0 },
+ [INSTR_RSY_AARD] = { 0xff, A_8,A_12,D20_20,B_16,0,0 },
+ [INSTR_RSY_CCRD] = { 0xff, C_8,C_12,D20_20,B_16,0,0 },
+ [INSTR_RSY_RRRD] = { 0xff, R_8,R_12,D20_20,B_16,0,0 },
+ [INSTR_RSY_RURD] = { 0xff, R_8,U4_12,D20_20,B_16,0,0 },
+ [INSTR_RSY_RDRM] = { 0xff, R_8,D20_20,B_16,U4_12,0,0 },
+ [INSTR_RS_AARD] = { 0xff, A_8,A_12,D_20,B_16,0,0 },
+ [INSTR_RS_CCRD] = { 0xff, C_8,C_12,D_20,B_16,0,0 },
+ [INSTR_RS_R0RD] = { 0xff, R_8,D_20,B_16,0,0,0 },
+ [INSTR_RS_RRRD] = { 0xff, R_8,R_12,D_20,B_16,0,0 },
+ [INSTR_RS_RURD] = { 0xff, R_8,U4_12,D_20,B_16,0,0 },
+ [INSTR_RXE_FRRD] = { 0xff, F_8,D_20,X_12,B_16,0,0 },
+ [INSTR_RXE_RRRD] = { 0xff, R_8,D_20,X_12,B_16,0,0 },
+ [INSTR_RXF_FRRDF] = { 0xff, F_32,F_8,D_20,X_12,B_16,0 },
+ [INSTR_RXY_FRRD] = { 0xff, F_8,D20_20,X_12,B_16,0,0 },
+ [INSTR_RXY_RRRD] = { 0xff, R_8,D20_20,X_12,B_16,0,0 },
+ [INSTR_RXY_URRD] = { 0xff, U4_8,D20_20,X_12,B_16,0,0 },
+ [INSTR_RX_FRRD] = { 0xff, F_8,D_20,X_12,B_16,0,0 },
+ [INSTR_RX_RRRD] = { 0xff, R_8,D_20,X_12,B_16,0,0 },
+ [INSTR_RX_URRD] = { 0xff, U4_8,D_20,X_12,B_16,0,0 },
+ [INSTR_SIL_RDI] = { 0xff, D_20,B_16,I16_32,0,0,0 },
+ [INSTR_SIL_RDU] = { 0xff, D_20,B_16,U16_32,0,0,0 },
+ [INSTR_SIY_IRD] = { 0xff, D20_20,B_16,I8_8,0,0,0 },
+ [INSTR_SIY_URD] = { 0xff, D20_20,B_16,U8_8,0,0,0 },
+ [INSTR_SI_URD] = { 0xff, D_20,B_16,U8_8,0,0,0 },
+ [INSTR_SSE_RDRD] = { 0xff, D_20,B_16,D_36,B_32,0,0 },
+ [INSTR_SSF_RRDRD] = { 0x00, D_20,B_16,D_36,B_32,R_8,0 },
+ [INSTR_SSF_RRDRD2]= { 0x00, R_8,D_20,B_16,D_36,B_32,0 },
+ [INSTR_SS_L0RDRD] = { 0xff, D_20,L8_8,B_16,D_36,B_32,0 },
+ [INSTR_SS_LIRDRD] = { 0xff, D_20,L4_8,B_16,D_36,B_32,U4_12 },
+ [INSTR_SS_LLRDRD] = { 0xff, D_20,L4_8,B_16,D_36,L4_12,B_32 },
+ [INSTR_SS_RRRDRD2]= { 0xff, R_8,D_20,B_16,R_12,D_36,B_32 },
+ [INSTR_SS_RRRDRD3]= { 0xff, R_8,R_12,D_20,B_16,D_36,B_32 },
+ [INSTR_SS_RRRDRD] = { 0xff, D_20,R_8,B_16,D_36,B_32,R_12 },
+ [INSTR_S_00] = { 0xff, 0,0,0,0,0,0 },
+ [INSTR_S_RD] = { 0xff, D_20,B_16,0,0,0,0 },
+};
+
+enum {
+ LONG_INSN_ALGHSIK,
+ LONG_INSN_ALHSIK,
+ LONG_INSN_CLFHSI,
+ LONG_INSN_CLGFRL,
+ LONG_INSN_CLGHRL,
+ LONG_INSN_CLGHSI,
+ LONG_INSN_CLHHSI,
+ LONG_INSN_LLGFRL,
+ LONG_INSN_LLGHRL,
+ LONG_INSN_POPCNT,
+ LONG_INSN_RISBHG,
+ LONG_INSN_RISBLG,
+};
+
+static char *long_insn_name[] = {
+ [LONG_INSN_ALGHSIK] = "alghsik",
+ [LONG_INSN_ALHSIK] = "alhsik",
+ [LONG_INSN_CLFHSI] = "clfhsi",
+ [LONG_INSN_CLGFRL] = "clgfrl",
+ [LONG_INSN_CLGHRL] = "clghrl",
+ [LONG_INSN_CLGHSI] = "clghsi",
+ [LONG_INSN_CLHHSI] = "clhhsi",
+ [LONG_INSN_LLGFRL] = "llgfrl",
+ [LONG_INSN_LLGHRL] = "llghrl",
+ [LONG_INSN_POPCNT] = "popcnt",
+ [LONG_INSN_RISBHG] = "risbhg",
+ [LONG_INSN_RISBLG] = "risblk",
+};
+
+static struct insn opcode[] = {
+#ifdef CONFIG_64BIT
+ { "lmd", 0xef, INSTR_SS_RRRDRD3 },
+#endif
+ { "spm", 0x04, INSTR_RR_R0 },
+ { "balr", 0x05, INSTR_RR_RR },
+ { "bctr", 0x06, INSTR_RR_RR },
+ { "bcr", 0x07, INSTR_RR_UR },
+ { "svc", 0x0a, INSTR_RR_U0 },
+ { "bsm", 0x0b, INSTR_RR_RR },
+ { "bassm", 0x0c, INSTR_RR_RR },
+ { "basr", 0x0d, INSTR_RR_RR },
+ { "mvcl", 0x0e, INSTR_RR_RR },
+ { "clcl", 0x0f, INSTR_RR_RR },
+ { "lpr", 0x10, INSTR_RR_RR },
+ { "lnr", 0x11, INSTR_RR_RR },
+ { "ltr", 0x12, INSTR_RR_RR },
+ { "lcr", 0x13, INSTR_RR_RR },
+ { "nr", 0x14, INSTR_RR_RR },
+ { "clr", 0x15, INSTR_RR_RR },
+ { "or", 0x16, INSTR_RR_RR },
+ { "xr", 0x17, INSTR_RR_RR },
+ { "lr", 0x18, INSTR_RR_RR },
+ { "cr", 0x19, INSTR_RR_RR },
+ { "ar", 0x1a, INSTR_RR_RR },
+ { "sr", 0x1b, INSTR_RR_RR },
+ { "mr", 0x1c, INSTR_RR_RR },
+ { "dr", 0x1d, INSTR_RR_RR },
+ { "alr", 0x1e, INSTR_RR_RR },
+ { "slr", 0x1f, INSTR_RR_RR },
+ { "lpdr", 0x20, INSTR_RR_FF },
+ { "lndr", 0x21, INSTR_RR_FF },
+ { "ltdr", 0x22, INSTR_RR_FF },
+ { "lcdr", 0x23, INSTR_RR_FF },
+ { "hdr", 0x24, INSTR_RR_FF },
+ { "ldxr", 0x25, INSTR_RR_FF },
+ { "lrdr", 0x25, INSTR_RR_FF },
+ { "mxr", 0x26, INSTR_RR_FF },
+ { "mxdr", 0x27, INSTR_RR_FF },
+ { "ldr", 0x28, INSTR_RR_FF },
+ { "cdr", 0x29, INSTR_RR_FF },
+ { "adr", 0x2a, INSTR_RR_FF },
+ { "sdr", 0x2b, INSTR_RR_FF },
+ { "mdr", 0x2c, INSTR_RR_FF },
+ { "ddr", 0x2d, INSTR_RR_FF },
+ { "awr", 0x2e, INSTR_RR_FF },
+ { "swr", 0x2f, INSTR_RR_FF },
+ { "lper", 0x30, INSTR_RR_FF },
+ { "lner", 0x31, INSTR_RR_FF },
+ { "lter", 0x32, INSTR_RR_FF },
+ { "lcer", 0x33, INSTR_RR_FF },
+ { "her", 0x34, INSTR_RR_FF },
+ { "ledr", 0x35, INSTR_RR_FF },
+ { "lrer", 0x35, INSTR_RR_FF },
+ { "axr", 0x36, INSTR_RR_FF },
+ { "sxr", 0x37, INSTR_RR_FF },
+ { "ler", 0x38, INSTR_RR_FF },
+ { "cer", 0x39, INSTR_RR_FF },
+ { "aer", 0x3a, INSTR_RR_FF },
+ { "ser", 0x3b, INSTR_RR_FF },
+ { "mder", 0x3c, INSTR_RR_FF },
+ { "mer", 0x3c, INSTR_RR_FF },
+ { "der", 0x3d, INSTR_RR_FF },
+ { "aur", 0x3e, INSTR_RR_FF },
+ { "sur", 0x3f, INSTR_RR_FF },
+ { "sth", 0x40, INSTR_RX_RRRD },
+ { "la", 0x41, INSTR_RX_RRRD },
+ { "stc", 0x42, INSTR_RX_RRRD },
+ { "ic", 0x43, INSTR_RX_RRRD },
+ { "ex", 0x44, INSTR_RX_RRRD },
+ { "bal", 0x45, INSTR_RX_RRRD },
+ { "bct", 0x46, INSTR_RX_RRRD },
+ { "bc", 0x47, INSTR_RX_URRD },
+ { "lh", 0x48, INSTR_RX_RRRD },
+ { "ch", 0x49, INSTR_RX_RRRD },
+ { "ah", 0x4a, INSTR_RX_RRRD },
+ { "sh", 0x4b, INSTR_RX_RRRD },
+ { "mh", 0x4c, INSTR_RX_RRRD },
+ { "bas", 0x4d, INSTR_RX_RRRD },
+ { "cvd", 0x4e, INSTR_RX_RRRD },
+ { "cvb", 0x4f, INSTR_RX_RRRD },
+ { "st", 0x50, INSTR_RX_RRRD },
+ { "lae", 0x51, INSTR_RX_RRRD },
+ { "n", 0x54, INSTR_RX_RRRD },
+ { "cl", 0x55, INSTR_RX_RRRD },
+ { "o", 0x56, INSTR_RX_RRRD },
+ { "x", 0x57, INSTR_RX_RRRD },
+ { "l", 0x58, INSTR_RX_RRRD },
+ { "c", 0x59, INSTR_RX_RRRD },
+ { "a", 0x5a, INSTR_RX_RRRD },
+ { "s", 0x5b, INSTR_RX_RRRD },
+ { "m", 0x5c, INSTR_RX_RRRD },
+ { "d", 0x5d, INSTR_RX_RRRD },
+ { "al", 0x5e, INSTR_RX_RRRD },
+ { "sl", 0x5f, INSTR_RX_RRRD },
+ { "std", 0x60, INSTR_RX_FRRD },
+ { "mxd", 0x67, INSTR_RX_FRRD },
+ { "ld", 0x68, INSTR_RX_FRRD },
+ { "cd", 0x69, INSTR_RX_FRRD },
+ { "ad", 0x6a, INSTR_RX_FRRD },
+ { "sd", 0x6b, INSTR_RX_FRRD },
+ { "md", 0x6c, INSTR_RX_FRRD },
+ { "dd", 0x6d, INSTR_RX_FRRD },
+ { "aw", 0x6e, INSTR_RX_FRRD },
+ { "sw", 0x6f, INSTR_RX_FRRD },
+ { "ste", 0x70, INSTR_RX_FRRD },
+ { "ms", 0x71, INSTR_RX_RRRD },
+ { "le", 0x78, INSTR_RX_FRRD },
+ { "ce", 0x79, INSTR_RX_FRRD },
+ { "ae", 0x7a, INSTR_RX_FRRD },
+ { "se", 0x7b, INSTR_RX_FRRD },
+ { "mde", 0x7c, INSTR_RX_FRRD },
+ { "me", 0x7c, INSTR_RX_FRRD },
+ { "de", 0x7d, INSTR_RX_FRRD },
+ { "au", 0x7e, INSTR_RX_FRRD },
+ { "su", 0x7f, INSTR_RX_FRRD },
+ { "ssm", 0x80, INSTR_S_RD },
+ { "lpsw", 0x82, INSTR_S_RD },
+ { "diag", 0x83, INSTR_RS_RRRD },
+ { "brxh", 0x84, INSTR_RSI_RRP },
+ { "brxle", 0x85, INSTR_RSI_RRP },
+ { "bxh", 0x86, INSTR_RS_RRRD },
+ { "bxle", 0x87, INSTR_RS_RRRD },
+ { "srl", 0x88, INSTR_RS_R0RD },
+ { "sll", 0x89, INSTR_RS_R0RD },
+ { "sra", 0x8a, INSTR_RS_R0RD },
+ { "sla", 0x8b, INSTR_RS_R0RD },
+ { "srdl", 0x8c, INSTR_RS_R0RD },
+ { "sldl", 0x8d, INSTR_RS_R0RD },
+ { "srda", 0x8e, INSTR_RS_R0RD },
+ { "slda", 0x8f, INSTR_RS_R0RD },
+ { "stm", 0x90, INSTR_RS_RRRD },
+ { "tm", 0x91, INSTR_SI_URD },
+ { "mvi", 0x92, INSTR_SI_URD },
+ { "ts", 0x93, INSTR_S_RD },
+ { "ni", 0x94, INSTR_SI_URD },
+ { "cli", 0x95, INSTR_SI_URD },
+ { "oi", 0x96, INSTR_SI_URD },
+ { "xi", 0x97, INSTR_SI_URD },
+ { "lm", 0x98, INSTR_RS_RRRD },
+ { "trace", 0x99, INSTR_RS_RRRD },
+ { "lam", 0x9a, INSTR_RS_AARD },
+ { "stam", 0x9b, INSTR_RS_AARD },
+ { "mvcle", 0xa8, INSTR_RS_RRRD },
+ { "clcle", 0xa9, INSTR_RS_RRRD },
+ { "stnsm", 0xac, INSTR_SI_URD },
+ { "stosm", 0xad, INSTR_SI_URD },
+ { "sigp", 0xae, INSTR_RS_RRRD },
+ { "mc", 0xaf, INSTR_SI_URD },
+ { "lra", 0xb1, INSTR_RX_RRRD },
+ { "stctl", 0xb6, INSTR_RS_CCRD },
+ { "lctl", 0xb7, INSTR_RS_CCRD },
+ { "cs", 0xba, INSTR_RS_RRRD },
+ { "cds", 0xbb, INSTR_RS_RRRD },
+ { "clm", 0xbd, INSTR_RS_RURD },
+ { "stcm", 0xbe, INSTR_RS_RURD },
+ { "icm", 0xbf, INSTR_RS_RURD },
+ { "mvn", 0xd1, INSTR_SS_L0RDRD },
+ { "mvc", 0xd2, INSTR_SS_L0RDRD },
+ { "mvz", 0xd3, INSTR_SS_L0RDRD },
+ { "nc", 0xd4, INSTR_SS_L0RDRD },
+ { "clc", 0xd5, INSTR_SS_L0RDRD },
+ { "oc", 0xd6, INSTR_SS_L0RDRD },
+ { "xc", 0xd7, INSTR_SS_L0RDRD },
+ { "mvck", 0xd9, INSTR_SS_RRRDRD },
+ { "mvcp", 0xda, INSTR_SS_RRRDRD },
+ { "mvcs", 0xdb, INSTR_SS_RRRDRD },
+ { "tr", 0xdc, INSTR_SS_L0RDRD },
+ { "trt", 0xdd, INSTR_SS_L0RDRD },
+ { "ed", 0xde, INSTR_SS_L0RDRD },
+ { "edmk", 0xdf, INSTR_SS_L0RDRD },
+ { "pku", 0xe1, INSTR_SS_L0RDRD },
+ { "unpku", 0xe2, INSTR_SS_L0RDRD },
+ { "mvcin", 0xe8, INSTR_SS_L0RDRD },
+ { "pka", 0xe9, INSTR_SS_L0RDRD },
+ { "unpka", 0xea, INSTR_SS_L0RDRD },
+ { "plo", 0xee, INSTR_SS_RRRDRD2 },
+ { "srp", 0xf0, INSTR_SS_LIRDRD },
+ { "mvo", 0xf1, INSTR_SS_LLRDRD },
+ { "pack", 0xf2, INSTR_SS_LLRDRD },
+ { "unpk", 0xf3, INSTR_SS_LLRDRD },
+ { "zap", 0xf8, INSTR_SS_LLRDRD },
+ { "cp", 0xf9, INSTR_SS_LLRDRD },
+ { "ap", 0xfa, INSTR_SS_LLRDRD },
+ { "sp", 0xfb, INSTR_SS_LLRDRD },
+ { "mp", 0xfc, INSTR_SS_LLRDRD },
+ { "dp", 0xfd, INSTR_SS_LLRDRD },
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_01[] = {
+#ifdef CONFIG_64BIT
+ { "sam64", 0x0e, INSTR_E },
+ { "pfpo", 0x0a, INSTR_E },
+ { "ptff", 0x04, INSTR_E },
+#endif
+ { "pr", 0x01, INSTR_E },
+ { "upt", 0x02, INSTR_E },
+ { "sckpf", 0x07, INSTR_E },
+ { "tam", 0x0b, INSTR_E },
+ { "sam24", 0x0c, INSTR_E },
+ { "sam31", 0x0d, INSTR_E },
+ { "trap2", 0xff, INSTR_E },
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_a5[] = {
+#ifdef CONFIG_64BIT
+ { "iihh", 0x00, INSTR_RI_RU },
+ { "iihl", 0x01, INSTR_RI_RU },
+ { "iilh", 0x02, INSTR_RI_RU },
+ { "iill", 0x03, INSTR_RI_RU },
+ { "nihh", 0x04, INSTR_RI_RU },
+ { "nihl", 0x05, INSTR_RI_RU },
+ { "nilh", 0x06, INSTR_RI_RU },
+ { "nill", 0x07, INSTR_RI_RU },
+ { "oihh", 0x08, INSTR_RI_RU },
+ { "oihl", 0x09, INSTR_RI_RU },
+ { "oilh", 0x0a, INSTR_RI_RU },
+ { "oill", 0x0b, INSTR_RI_RU },
+ { "llihh", 0x0c, INSTR_RI_RU },
+ { "llihl", 0x0d, INSTR_RI_RU },
+ { "llilh", 0x0e, INSTR_RI_RU },
+ { "llill", 0x0f, INSTR_RI_RU },
+#endif
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_a7[] = {
+#ifdef CONFIG_64BIT
+ { "tmhh", 0x02, INSTR_RI_RU },
+ { "tmhl", 0x03, INSTR_RI_RU },
+ { "brctg", 0x07, INSTR_RI_RP },
+ { "lghi", 0x09, INSTR_RI_RI },
+ { "aghi", 0x0b, INSTR_RI_RI },
+ { "mghi", 0x0d, INSTR_RI_RI },
+ { "cghi", 0x0f, INSTR_RI_RI },
+#endif
+ { "tmlh", 0x00, INSTR_RI_RU },
+ { "tmll", 0x01, INSTR_RI_RU },
+ { "brc", 0x04, INSTR_RI_UP },
+ { "bras", 0x05, INSTR_RI_RP },
+ { "brct", 0x06, INSTR_RI_RP },
+ { "lhi", 0x08, INSTR_RI_RI },
+ { "ahi", 0x0a, INSTR_RI_RI },
+ { "mhi", 0x0c, INSTR_RI_RI },
+ { "chi", 0x0e, INSTR_RI_RI },
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_b2[] = {
+#ifdef CONFIG_64BIT
+ { "sske", 0x2b, INSTR_RRF_M0RR },
+ { "stckf", 0x7c, INSTR_S_RD },
+ { "cu21", 0xa6, INSTR_RRF_M0RR },
+ { "cuutf", 0xa6, INSTR_RRF_M0RR },
+ { "cu12", 0xa7, INSTR_RRF_M0RR },
+ { "cutfu", 0xa7, INSTR_RRF_M0RR },
+ { "stfle", 0xb0, INSTR_S_RD },
+ { "lpswe", 0xb2, INSTR_S_RD },
+ { "srnmt", 0xb9, INSTR_S_RD },
+ { "lfas", 0xbd, INSTR_S_RD },
+#endif
+ { "stidp", 0x02, INSTR_S_RD },
+ { "sck", 0x04, INSTR_S_RD },
+ { "stck", 0x05, INSTR_S_RD },
+ { "sckc", 0x06, INSTR_S_RD },
+ { "stckc", 0x07, INSTR_S_RD },
+ { "spt", 0x08, INSTR_S_RD },
+ { "stpt", 0x09, INSTR_S_RD },
+ { "spka", 0x0a, INSTR_S_RD },
+ { "ipk", 0x0b, INSTR_S_00 },
+ { "ptlb", 0x0d, INSTR_S_00 },
+ { "spx", 0x10, INSTR_S_RD },
+ { "stpx", 0x11, INSTR_S_RD },
+ { "stap", 0x12, INSTR_S_RD },
+ { "sie", 0x14, INSTR_S_RD },
+ { "pc", 0x18, INSTR_S_RD },
+ { "sac", 0x19, INSTR_S_RD },
+ { "cfc", 0x1a, INSTR_S_RD },
+ { "ipte", 0x21, INSTR_RRE_RR },
+ { "ipm", 0x22, INSTR_RRE_R0 },
+ { "ivsk", 0x23, INSTR_RRE_RR },
+ { "iac", 0x24, INSTR_RRE_R0 },
+ { "ssar", 0x25, INSTR_RRE_R0 },
+ { "epar", 0x26, INSTR_RRE_R0 },
+ { "esar", 0x27, INSTR_RRE_R0 },
+ { "pt", 0x28, INSTR_RRE_RR },
+ { "iske", 0x29, INSTR_RRE_RR },
+ { "rrbe", 0x2a, INSTR_RRE_RR },
+ { "sske", 0x2b, INSTR_RRE_RR },
+ { "tb", 0x2c, INSTR_RRE_0R },
+ { "dxr", 0x2d, INSTR_RRE_F0 },
+ { "pgin", 0x2e, INSTR_RRE_RR },
+ { "pgout", 0x2f, INSTR_RRE_RR },
+ { "csch", 0x30, INSTR_S_00 },
+ { "hsch", 0x31, INSTR_S_00 },
+ { "msch", 0x32, INSTR_S_RD },
+ { "ssch", 0x33, INSTR_S_RD },
+ { "stsch", 0x34, INSTR_S_RD },
+ { "tsch", 0x35, INSTR_S_RD },
+ { "tpi", 0x36, INSTR_S_RD },
+ { "sal", 0x37, INSTR_S_00 },
+ { "rsch", 0x38, INSTR_S_00 },
+ { "stcrw", 0x39, INSTR_S_RD },
+ { "stcps", 0x3a, INSTR_S_RD },
+ { "rchp", 0x3b, INSTR_S_00 },
+ { "schm", 0x3c, INSTR_S_00 },
+ { "bakr", 0x40, INSTR_RRE_RR },
+ { "cksm", 0x41, INSTR_RRE_RR },
+ { "sqdr", 0x44, INSTR_RRE_F0 },
+ { "sqer", 0x45, INSTR_RRE_F0 },
+ { "stura", 0x46, INSTR_RRE_RR },
+ { "msta", 0x47, INSTR_RRE_R0 },
+ { "palb", 0x48, INSTR_RRE_00 },
+ { "ereg", 0x49, INSTR_RRE_RR },
+ { "esta", 0x4a, INSTR_RRE_RR },
+ { "lura", 0x4b, INSTR_RRE_RR },
+ { "tar", 0x4c, INSTR_RRE_AR },
+ { "cpya", 0x4d, INSTR_RRE_AA },
+ { "sar", 0x4e, INSTR_RRE_AR },
+ { "ear", 0x4f, INSTR_RRE_RA },
+ { "csp", 0x50, INSTR_RRE_RR },
+ { "msr", 0x52, INSTR_RRE_RR },
+ { "mvpg", 0x54, INSTR_RRE_RR },
+ { "mvst", 0x55, INSTR_RRE_RR },
+ { "cuse", 0x57, INSTR_RRE_RR },
+ { "bsg", 0x58, INSTR_RRE_RR },
+ { "bsa", 0x5a, INSTR_RRE_RR },
+ { "clst", 0x5d, INSTR_RRE_RR },
+ { "srst", 0x5e, INSTR_RRE_RR },
+ { "cmpsc", 0x63, INSTR_RRE_RR },
+ { "siga", 0x74, INSTR_S_RD },
+ { "xsch", 0x76, INSTR_S_00 },
+ { "rp", 0x77, INSTR_S_RD },
+ { "stcke", 0x78, INSTR_S_RD },
+ { "sacf", 0x79, INSTR_S_RD },
+ { "spp", 0x80, INSTR_S_RD },
+ { "stsi", 0x7d, INSTR_S_RD },
+ { "srnm", 0x99, INSTR_S_RD },
+ { "stfpc", 0x9c, INSTR_S_RD },
+ { "lfpc", 0x9d, INSTR_S_RD },
+ { "tre", 0xa5, INSTR_RRE_RR },
+ { "cuutf", 0xa6, INSTR_RRE_RR },
+ { "cutfu", 0xa7, INSTR_RRE_RR },
+ { "stfl", 0xb1, INSTR_S_RD },
+ { "trap4", 0xff, INSTR_S_RD },
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_b3[] = {
+#ifdef CONFIG_64BIT
+ { "maylr", 0x38, INSTR_RRF_F0FF },
+ { "mylr", 0x39, INSTR_RRF_F0FF },
+ { "mayr", 0x3a, INSTR_RRF_F0FF },
+ { "myr", 0x3b, INSTR_RRF_F0FF },
+ { "mayhr", 0x3c, INSTR_RRF_F0FF },
+ { "myhr", 0x3d, INSTR_RRF_F0FF },
+ { "cegbr", 0xa4, INSTR_RRE_RR },
+ { "cdgbr", 0xa5, INSTR_RRE_RR },
+ { "cxgbr", 0xa6, INSTR_RRE_RR },
+ { "cgebr", 0xa8, INSTR_RRF_U0RF },
+ { "cgdbr", 0xa9, INSTR_RRF_U0RF },
+ { "cgxbr", 0xaa, INSTR_RRF_U0RF },
+ { "cfer", 0xb8, INSTR_RRF_U0RF },
+ { "cfdr", 0xb9, INSTR_RRF_U0RF },
+ { "cfxr", 0xba, INSTR_RRF_U0RF },
+ { "cegr", 0xc4, INSTR_RRE_RR },
+ { "cdgr", 0xc5, INSTR_RRE_RR },
+ { "cxgr", 0xc6, INSTR_RRE_RR },
+ { "cger", 0xc8, INSTR_RRF_U0RF },
+ { "cgdr", 0xc9, INSTR_RRF_U0RF },
+ { "cgxr", 0xca, INSTR_RRF_U0RF },
+ { "lpdfr", 0x70, INSTR_RRE_FF },
+ { "lndfr", 0x71, INSTR_RRE_FF },
+ { "cpsdr", 0x72, INSTR_RRF_F0FF2 },
+ { "lcdfr", 0x73, INSTR_RRE_FF },
+ { "ldgr", 0xc1, INSTR_RRE_FR },
+ { "lgdr", 0xcd, INSTR_RRE_RF },
+ { "adtr", 0xd2, INSTR_RRR_F0FF },
+ { "axtr", 0xda, INSTR_RRR_F0FF },
+ { "cdtr", 0xe4, INSTR_RRE_FF },
+ { "cxtr", 0xec, INSTR_RRE_FF },
+ { "kdtr", 0xe0, INSTR_RRE_FF },
+ { "kxtr", 0xe8, INSTR_RRE_FF },
+ { "cedtr", 0xf4, INSTR_RRE_FF },
+ { "cextr", 0xfc, INSTR_RRE_FF },
+ { "cdgtr", 0xf1, INSTR_RRE_FR },
+ { "cxgtr", 0xf9, INSTR_RRE_FR },
+ { "cdstr", 0xf3, INSTR_RRE_FR },
+ { "cxstr", 0xfb, INSTR_RRE_FR },
+ { "cdutr", 0xf2, INSTR_RRE_FR },
+ { "cxutr", 0xfa, INSTR_RRE_FR },
+ { "cgdtr", 0xe1, INSTR_RRF_U0RF },
+ { "cgxtr", 0xe9, INSTR_RRF_U0RF },
+ { "csdtr", 0xe3, INSTR_RRE_RF },
+ { "csxtr", 0xeb, INSTR_RRE_RF },
+ { "cudtr", 0xe2, INSTR_RRE_RF },
+ { "cuxtr", 0xea, INSTR_RRE_RF },
+ { "ddtr", 0xd1, INSTR_RRR_F0FF },
+ { "dxtr", 0xd9, INSTR_RRR_F0FF },
+ { "eedtr", 0xe5, INSTR_RRE_RF },
+ { "eextr", 0xed, INSTR_RRE_RF },
+ { "esdtr", 0xe7, INSTR_RRE_RF },
+ { "esxtr", 0xef, INSTR_RRE_RF },
+ { "iedtr", 0xf6, INSTR_RRF_F0FR },
+ { "iextr", 0xfe, INSTR_RRF_F0FR },
+ { "ltdtr", 0xd6, INSTR_RRE_FF },
+ { "ltxtr", 0xde, INSTR_RRE_FF },
+ { "fidtr", 0xd7, INSTR_RRF_UUFF },
+ { "fixtr", 0xdf, INSTR_RRF_UUFF },
+ { "ldetr", 0xd4, INSTR_RRF_0UFF },
+ { "lxdtr", 0xdc, INSTR_RRF_0UFF },
+ { "ledtr", 0xd5, INSTR_RRF_UUFF },
+ { "ldxtr", 0xdd, INSTR_RRF_UUFF },
+ { "mdtr", 0xd0, INSTR_RRR_F0FF },
+ { "mxtr", 0xd8, INSTR_RRR_F0FF },
+ { "qadtr", 0xf5, INSTR_RRF_FUFF },
+ { "qaxtr", 0xfd, INSTR_RRF_FUFF },
+ { "rrdtr", 0xf7, INSTR_RRF_FFRU },
+ { "rrxtr", 0xff, INSTR_RRF_FFRU },
+ { "sfasr", 0x85, INSTR_RRE_R0 },
+ { "sdtr", 0xd3, INSTR_RRR_F0FF },
+ { "sxtr", 0xdb, INSTR_RRR_F0FF },
+#endif
+ { "lpebr", 0x00, INSTR_RRE_FF },
+ { "lnebr", 0x01, INSTR_RRE_FF },
+ { "ltebr", 0x02, INSTR_RRE_FF },
+ { "lcebr", 0x03, INSTR_RRE_FF },
+ { "ldebr", 0x04, INSTR_RRE_FF },
+ { "lxdbr", 0x05, INSTR_RRE_FF },
+ { "lxebr", 0x06, INSTR_RRE_FF },
+ { "mxdbr", 0x07, INSTR_RRE_FF },
+ { "kebr", 0x08, INSTR_RRE_FF },
+ { "cebr", 0x09, INSTR_RRE_FF },
+ { "aebr", 0x0a, INSTR_RRE_FF },
+ { "sebr", 0x0b, INSTR_RRE_FF },
+ { "mdebr", 0x0c, INSTR_RRE_FF },
+ { "debr", 0x0d, INSTR_RRE_FF },
+ { "maebr", 0x0e, INSTR_RRF_F0FF },
+ { "msebr", 0x0f, INSTR_RRF_F0FF },
+ { "lpdbr", 0x10, INSTR_RRE_FF },
+ { "lndbr", 0x11, INSTR_RRE_FF },
+ { "ltdbr", 0x12, INSTR_RRE_FF },
+ { "lcdbr", 0x13, INSTR_RRE_FF },
+ { "sqebr", 0x14, INSTR_RRE_FF },
+ { "sqdbr", 0x15, INSTR_RRE_FF },
+ { "sqxbr", 0x16, INSTR_RRE_FF },
+ { "meebr", 0x17, INSTR_RRE_FF },
+ { "kdbr", 0x18, INSTR_RRE_FF },
+ { "cdbr", 0x19, INSTR_RRE_FF },
+ { "adbr", 0x1a, INSTR_RRE_FF },
+ { "sdbr", 0x1b, INSTR_RRE_FF },
+ { "mdbr", 0x1c, INSTR_RRE_FF },
+ { "ddbr", 0x1d, INSTR_RRE_FF },
+ { "madbr", 0x1e, INSTR_RRF_F0FF },
+ { "msdbr", 0x1f, INSTR_RRF_F0FF },
+ { "lder", 0x24, INSTR_RRE_FF },
+ { "lxdr", 0x25, INSTR_RRE_FF },
+ { "lxer", 0x26, INSTR_RRE_FF },
+ { "maer", 0x2e, INSTR_RRF_F0FF },
+ { "mser", 0x2f, INSTR_RRF_F0FF },
+ { "sqxr", 0x36, INSTR_RRE_FF },
+ { "meer", 0x37, INSTR_RRE_FF },
+ { "madr", 0x3e, INSTR_RRF_F0FF },
+ { "msdr", 0x3f, INSTR_RRF_F0FF },
+ { "lpxbr", 0x40, INSTR_RRE_FF },
+ { "lnxbr", 0x41, INSTR_RRE_FF },
+ { "ltxbr", 0x42, INSTR_RRE_FF },
+ { "lcxbr", 0x43, INSTR_RRE_FF },
+ { "ledbr", 0x44, INSTR_RRE_FF },
+ { "ldxbr", 0x45, INSTR_RRE_FF },
+ { "lexbr", 0x46, INSTR_RRE_FF },
+ { "fixbr", 0x47, INSTR_RRF_U0FF },
+ { "kxbr", 0x48, INSTR_RRE_FF },
+ { "cxbr", 0x49, INSTR_RRE_FF },
+ { "axbr", 0x4a, INSTR_RRE_FF },
+ { "sxbr", 0x4b, INSTR_RRE_FF },
+ { "mxbr", 0x4c, INSTR_RRE_FF },
+ { "dxbr", 0x4d, INSTR_RRE_FF },
+ { "tbedr", 0x50, INSTR_RRF_U0FF },
+ { "tbdr", 0x51, INSTR_RRF_U0FF },
+ { "diebr", 0x53, INSTR_RRF_FUFF },
+ { "fiebr", 0x57, INSTR_RRF_U0FF },
+ { "thder", 0x58, INSTR_RRE_RR },
+ { "thdr", 0x59, INSTR_RRE_RR },
+ { "didbr", 0x5b, INSTR_RRF_FUFF },
+ { "fidbr", 0x5f, INSTR_RRF_U0FF },
+ { "lpxr", 0x60, INSTR_RRE_FF },
+ { "lnxr", 0x61, INSTR_RRE_FF },
+ { "ltxr", 0x62, INSTR_RRE_FF },
+ { "lcxr", 0x63, INSTR_RRE_FF },
+ { "lxr", 0x65, INSTR_RRE_RR },
+ { "lexr", 0x66, INSTR_RRE_FF },
+ { "fixr", 0x67, INSTR_RRF_U0FF },
+ { "cxr", 0x69, INSTR_RRE_FF },
+ { "lzer", 0x74, INSTR_RRE_R0 },
+ { "lzdr", 0x75, INSTR_RRE_R0 },
+ { "lzxr", 0x76, INSTR_RRE_R0 },
+ { "fier", 0x77, INSTR_RRF_U0FF },
+ { "fidr", 0x7f, INSTR_RRF_U0FF },
+ { "sfpc", 0x84, INSTR_RRE_RR_OPT },
+ { "efpc", 0x8c, INSTR_RRE_RR_OPT },
+ { "cefbr", 0x94, INSTR_RRE_RF },
+ { "cdfbr", 0x95, INSTR_RRE_RF },
+ { "cxfbr", 0x96, INSTR_RRE_RF },
+ { "cfebr", 0x98, INSTR_RRF_U0RF },
+ { "cfdbr", 0x99, INSTR_RRF_U0RF },
+ { "cfxbr", 0x9a, INSTR_RRF_U0RF },
+ { "cefr", 0xb4, INSTR_RRE_RF },
+ { "cdfr", 0xb5, INSTR_RRE_RF },
+ { "cxfr", 0xb6, INSTR_RRE_RF },
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_b9[] = {
+#ifdef CONFIG_64BIT
+ { "lpgr", 0x00, INSTR_RRE_RR },
+ { "lngr", 0x01, INSTR_RRE_RR },
+ { "ltgr", 0x02, INSTR_RRE_RR },
+ { "lcgr", 0x03, INSTR_RRE_RR },
+ { "lgr", 0x04, INSTR_RRE_RR },
+ { "lurag", 0x05, INSTR_RRE_RR },
+ { "lgbr", 0x06, INSTR_RRE_RR },
+ { "lghr", 0x07, INSTR_RRE_RR },
+ { "agr", 0x08, INSTR_RRE_RR },
+ { "sgr", 0x09, INSTR_RRE_RR },
+ { "algr", 0x0a, INSTR_RRE_RR },
+ { "slgr", 0x0b, INSTR_RRE_RR },
+ { "msgr", 0x0c, INSTR_RRE_RR },
+ { "dsgr", 0x0d, INSTR_RRE_RR },
+ { "eregg", 0x0e, INSTR_RRE_RR },
+ { "lrvgr", 0x0f, INSTR_RRE_RR },
+ { "lpgfr", 0x10, INSTR_RRE_RR },
+ { "lngfr", 0x11, INSTR_RRE_RR },
+ { "ltgfr", 0x12, INSTR_RRE_RR },
+ { "lcgfr", 0x13, INSTR_RRE_RR },
+ { "lgfr", 0x14, INSTR_RRE_RR },
+ { "llgfr", 0x16, INSTR_RRE_RR },
+ { "llgtr", 0x17, INSTR_RRE_RR },
+ { "agfr", 0x18, INSTR_RRE_RR },
+ { "sgfr", 0x19, INSTR_RRE_RR },
+ { "algfr", 0x1a, INSTR_RRE_RR },
+ { "slgfr", 0x1b, INSTR_RRE_RR },
+ { "msgfr", 0x1c, INSTR_RRE_RR },
+ { "dsgfr", 0x1d, INSTR_RRE_RR },
+ { "cgr", 0x20, INSTR_RRE_RR },
+ { "clgr", 0x21, INSTR_RRE_RR },
+ { "sturg", 0x25, INSTR_RRE_RR },
+ { "lbr", 0x26, INSTR_RRE_RR },
+ { "lhr", 0x27, INSTR_RRE_RR },
+ { "cgfr", 0x30, INSTR_RRE_RR },
+ { "clgfr", 0x31, INSTR_RRE_RR },
+ { "bctgr", 0x46, INSTR_RRE_RR },
+ { "ngr", 0x80, INSTR_RRE_RR },
+ { "ogr", 0x81, INSTR_RRE_RR },
+ { "xgr", 0x82, INSTR_RRE_RR },
+ { "flogr", 0x83, INSTR_RRE_RR },
+ { "llgcr", 0x84, INSTR_RRE_RR },
+ { "llghr", 0x85, INSTR_RRE_RR },
+ { "mlgr", 0x86, INSTR_RRE_RR },
+ { "dlgr", 0x87, INSTR_RRE_RR },
+ { "alcgr", 0x88, INSTR_RRE_RR },
+ { "slbgr", 0x89, INSTR_RRE_RR },
+ { "cspg", 0x8a, INSTR_RRE_RR },
+ { "idte", 0x8e, INSTR_RRF_R0RR },
+ { "llcr", 0x94, INSTR_RRE_RR },
+ { "llhr", 0x95, INSTR_RRE_RR },
+ { "esea", 0x9d, INSTR_RRE_R0 },
+ { "lptea", 0xaa, INSTR_RRF_RURR },
+ { "cu14", 0xb0, INSTR_RRF_M0RR },
+ { "cu24", 0xb1, INSTR_RRF_M0RR },
+ { "cu41", 0xb2, INSTR_RRF_M0RR },
+ { "cu42", 0xb3, INSTR_RRF_M0RR },
+ { "crt", 0x72, INSTR_RRF_U0RR },
+ { "cgrt", 0x60, INSTR_RRF_U0RR },
+ { "clrt", 0x73, INSTR_RRF_U0RR },
+ { "clgrt", 0x61, INSTR_RRF_U0RR },
+ { "ptf", 0xa2, INSTR_RRE_R0 },
+ { "pfmf", 0xaf, INSTR_RRE_RR },
+ { "trte", 0xbf, INSTR_RRF_M0RR },
+ { "trtre", 0xbd, INSTR_RRF_M0RR },
+ { "ahhhr", 0xc8, INSTR_RRF_R0RR2 },
+ { "shhhr", 0xc9, INSTR_RRF_R0RR2 },
+ { "alhhh", 0xca, INSTR_RRF_R0RR2 },
+ { "alhhl", 0xca, INSTR_RRF_R0RR2 },
+ { "slhhh", 0xcb, INSTR_RRF_R0RR2 },
+ { "chhr ", 0xcd, INSTR_RRE_RR },
+ { "clhhr", 0xcf, INSTR_RRE_RR },
+ { "ahhlr", 0xd8, INSTR_RRF_R0RR2 },
+ { "shhlr", 0xd9, INSTR_RRF_R0RR2 },
+ { "slhhl", 0xdb, INSTR_RRF_R0RR2 },
+ { "chlr", 0xdd, INSTR_RRE_RR },
+ { "clhlr", 0xdf, INSTR_RRE_RR },
+ { { 0, LONG_INSN_POPCNT }, 0xe1, INSTR_RRE_RR },
+ { "locgr", 0xe2, INSTR_RRF_M0RR },
+ { "ngrk", 0xe4, INSTR_RRF_R0RR2 },
+ { "ogrk", 0xe6, INSTR_RRF_R0RR2 },
+ { "xgrk", 0xe7, INSTR_RRF_R0RR2 },
+ { "agrk", 0xe8, INSTR_RRF_R0RR2 },
+ { "sgrk", 0xe9, INSTR_RRF_R0RR2 },
+ { "algrk", 0xea, INSTR_RRF_R0RR2 },
+ { "slgrk", 0xeb, INSTR_RRF_R0RR2 },
+ { "locr", 0xf2, INSTR_RRF_M0RR },
+ { "nrk", 0xf4, INSTR_RRF_R0RR2 },
+ { "ork", 0xf6, INSTR_RRF_R0RR2 },
+ { "xrk", 0xf7, INSTR_RRF_R0RR2 },
+ { "ark", 0xf8, INSTR_RRF_R0RR2 },
+ { "srk", 0xf9, INSTR_RRF_R0RR2 },
+ { "alrk", 0xfa, INSTR_RRF_R0RR2 },
+ { "slrk", 0xfb, INSTR_RRF_R0RR2 },
+#endif
+ { "kmac", 0x1e, INSTR_RRE_RR },
+ { "lrvr", 0x1f, INSTR_RRE_RR },
+ { "km", 0x2e, INSTR_RRE_RR },
+ { "kmc", 0x2f, INSTR_RRE_RR },
+ { "kimd", 0x3e, INSTR_RRE_RR },
+ { "klmd", 0x3f, INSTR_RRE_RR },
+ { "epsw", 0x8d, INSTR_RRE_RR },
+ { "trtt", 0x90, INSTR_RRE_RR },
+ { "trtt", 0x90, INSTR_RRF_M0RR },
+ { "trto", 0x91, INSTR_RRE_RR },
+ { "trto", 0x91, INSTR_RRF_M0RR },
+ { "trot", 0x92, INSTR_RRE_RR },
+ { "trot", 0x92, INSTR_RRF_M0RR },
+ { "troo", 0x93, INSTR_RRE_RR },
+ { "troo", 0x93, INSTR_RRF_M0RR },
+ { "mlr", 0x96, INSTR_RRE_RR },
+ { "dlr", 0x97, INSTR_RRE_RR },
+ { "alcr", 0x98, INSTR_RRE_RR },
+ { "slbr", 0x99, INSTR_RRE_RR },
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_c0[] = {
+#ifdef CONFIG_64BIT
+ { "lgfi", 0x01, INSTR_RIL_RI },
+ { "xihf", 0x06, INSTR_RIL_RU },
+ { "xilf", 0x07, INSTR_RIL_RU },
+ { "iihf", 0x08, INSTR_RIL_RU },
+ { "iilf", 0x09, INSTR_RIL_RU },
+ { "nihf", 0x0a, INSTR_RIL_RU },
+ { "nilf", 0x0b, INSTR_RIL_RU },
+ { "oihf", 0x0c, INSTR_RIL_RU },
+ { "oilf", 0x0d, INSTR_RIL_RU },
+ { "llihf", 0x0e, INSTR_RIL_RU },
+ { "llilf", 0x0f, INSTR_RIL_RU },
+#endif
+ { "larl", 0x00, INSTR_RIL_RP },
+ { "brcl", 0x04, INSTR_RIL_UP },
+ { "brasl", 0x05, INSTR_RIL_RP },
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_c2[] = {
+#ifdef CONFIG_64BIT
+ { "slgfi", 0x04, INSTR_RIL_RU },
+ { "slfi", 0x05, INSTR_RIL_RU },
+ { "agfi", 0x08, INSTR_RIL_RI },
+ { "afi", 0x09, INSTR_RIL_RI },
+ { "algfi", 0x0a, INSTR_RIL_RU },
+ { "alfi", 0x0b, INSTR_RIL_RU },
+ { "cgfi", 0x0c, INSTR_RIL_RI },
+ { "cfi", 0x0d, INSTR_RIL_RI },
+ { "clgfi", 0x0e, INSTR_RIL_RU },
+ { "clfi", 0x0f, INSTR_RIL_RU },
+ { "msfi", 0x01, INSTR_RIL_RI },
+ { "msgfi", 0x00, INSTR_RIL_RI },
+#endif
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_c4[] = {
+#ifdef CONFIG_64BIT
+ { "lrl", 0x0d, INSTR_RIL_RP },
+ { "lgrl", 0x08, INSTR_RIL_RP },
+ { "lgfrl", 0x0c, INSTR_RIL_RP },
+ { "lhrl", 0x05, INSTR_RIL_RP },
+ { "lghrl", 0x04, INSTR_RIL_RP },
+ { { 0, LONG_INSN_LLGFRL }, 0x0e, INSTR_RIL_RP },
+ { "llhrl", 0x02, INSTR_RIL_RP },
+ { { 0, LONG_INSN_LLGHRL }, 0x06, INSTR_RIL_RP },
+ { "strl", 0x0f, INSTR_RIL_RP },
+ { "stgrl", 0x0b, INSTR_RIL_RP },
+ { "sthrl", 0x07, INSTR_RIL_RP },
+#endif
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_c6[] = {
+#ifdef CONFIG_64BIT
+ { "crl", 0x0d, INSTR_RIL_RP },
+ { "cgrl", 0x08, INSTR_RIL_RP },
+ { "cgfrl", 0x0c, INSTR_RIL_RP },
+ { "chrl", 0x05, INSTR_RIL_RP },
+ { "cghrl", 0x04, INSTR_RIL_RP },
+ { "clrl", 0x0f, INSTR_RIL_RP },
+ { "clgrl", 0x0a, INSTR_RIL_RP },
+ { { 0, LONG_INSN_CLGFRL }, 0x0e, INSTR_RIL_RP },
+ { "clhrl", 0x07, INSTR_RIL_RP },
+ { { 0, LONG_INSN_CLGHRL }, 0x06, INSTR_RIL_RP },
+ { "pfdrl", 0x02, INSTR_RIL_UP },
+ { "exrl", 0x00, INSTR_RIL_RP },
+#endif
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_c8[] = {
+#ifdef CONFIG_64BIT
+ { "mvcos", 0x00, INSTR_SSF_RRDRD },
+ { "ectg", 0x01, INSTR_SSF_RRDRD },
+ { "csst", 0x02, INSTR_SSF_RRDRD },
+ { "lpd", 0x04, INSTR_SSF_RRDRD2 },
+ { "lpdg ", 0x05, INSTR_SSF_RRDRD2 },
+#endif
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_cc[] = {
+#ifdef CONFIG_64BIT
+ { "brcth", 0x06, INSTR_RIL_RP },
+ { "aih", 0x08, INSTR_RIL_RI },
+ { "alsih", 0x0a, INSTR_RIL_RI },
+ { "alsih", 0x0b, INSTR_RIL_RI },
+ { "cih", 0x0d, INSTR_RIL_RI },
+ { "clih ", 0x0f, INSTR_RIL_RI },
+#endif
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_e3[] = {
+#ifdef CONFIG_64BIT
+ { "ltg", 0x02, INSTR_RXY_RRRD },
+ { "lrag", 0x03, INSTR_RXY_RRRD },
+ { "lg", 0x04, INSTR_RXY_RRRD },
+ { "cvby", 0x06, INSTR_RXY_RRRD },
+ { "ag", 0x08, INSTR_RXY_RRRD },
+ { "sg", 0x09, INSTR_RXY_RRRD },
+ { "alg", 0x0a, INSTR_RXY_RRRD },
+ { "slg", 0x0b, INSTR_RXY_RRRD },
+ { "msg", 0x0c, INSTR_RXY_RRRD },
+ { "dsg", 0x0d, INSTR_RXY_RRRD },
+ { "cvbg", 0x0e, INSTR_RXY_RRRD },
+ { "lrvg", 0x0f, INSTR_RXY_RRRD },
+ { "lt", 0x12, INSTR_RXY_RRRD },
+ { "lray", 0x13, INSTR_RXY_RRRD },
+ { "lgf", 0x14, INSTR_RXY_RRRD },
+ { "lgh", 0x15, INSTR_RXY_RRRD },
+ { "llgf", 0x16, INSTR_RXY_RRRD },
+ { "llgt", 0x17, INSTR_RXY_RRRD },
+ { "agf", 0x18, INSTR_RXY_RRRD },
+ { "sgf", 0x19, INSTR_RXY_RRRD },
+ { "algf", 0x1a, INSTR_RXY_RRRD },
+ { "slgf", 0x1b, INSTR_RXY_RRRD },
+ { "msgf", 0x1c, INSTR_RXY_RRRD },
+ { "dsgf", 0x1d, INSTR_RXY_RRRD },
+ { "cg", 0x20, INSTR_RXY_RRRD },
+ { "clg", 0x21, INSTR_RXY_RRRD },
+ { "stg", 0x24, INSTR_RXY_RRRD },
+ { "cvdy", 0x26, INSTR_RXY_RRRD },
+ { "cvdg", 0x2e, INSTR_RXY_RRRD },
+ { "strvg", 0x2f, INSTR_RXY_RRRD },
+ { "cgf", 0x30, INSTR_RXY_RRRD },
+ { "clgf", 0x31, INSTR_RXY_RRRD },
+ { "strvh", 0x3f, INSTR_RXY_RRRD },
+ { "bctg", 0x46, INSTR_RXY_RRRD },
+ { "sty", 0x50, INSTR_RXY_RRRD },
+ { "msy", 0x51, INSTR_RXY_RRRD },
+ { "ny", 0x54, INSTR_RXY_RRRD },
+ { "cly", 0x55, INSTR_RXY_RRRD },
+ { "oy", 0x56, INSTR_RXY_RRRD },
+ { "xy", 0x57, INSTR_RXY_RRRD },
+ { "ly", 0x58, INSTR_RXY_RRRD },
+ { "cy", 0x59, INSTR_RXY_RRRD },
+ { "ay", 0x5a, INSTR_RXY_RRRD },
+ { "sy", 0x5b, INSTR_RXY_RRRD },
+ { "aly", 0x5e, INSTR_RXY_RRRD },
+ { "sly", 0x5f, INSTR_RXY_RRRD },
+ { "sthy", 0x70, INSTR_RXY_RRRD },
+ { "lay", 0x71, INSTR_RXY_RRRD },
+ { "stcy", 0x72, INSTR_RXY_RRRD },
+ { "icy", 0x73, INSTR_RXY_RRRD },
+ { "lb", 0x76, INSTR_RXY_RRRD },
+ { "lgb", 0x77, INSTR_RXY_RRRD },
+ { "lhy", 0x78, INSTR_RXY_RRRD },
+ { "chy", 0x79, INSTR_RXY_RRRD },
+ { "ahy", 0x7a, INSTR_RXY_RRRD },
+ { "shy", 0x7b, INSTR_RXY_RRRD },
+ { "ng", 0x80, INSTR_RXY_RRRD },
+ { "og", 0x81, INSTR_RXY_RRRD },
+ { "xg", 0x82, INSTR_RXY_RRRD },
+ { "mlg", 0x86, INSTR_RXY_RRRD },
+ { "dlg", 0x87, INSTR_RXY_RRRD },
+ { "alcg", 0x88, INSTR_RXY_RRRD },
+ { "slbg", 0x89, INSTR_RXY_RRRD },
+ { "stpq", 0x8e, INSTR_RXY_RRRD },
+ { "lpq", 0x8f, INSTR_RXY_RRRD },
+ { "llgc", 0x90, INSTR_RXY_RRRD },
+ { "llgh", 0x91, INSTR_RXY_RRRD },
+ { "llc", 0x94, INSTR_RXY_RRRD },
+ { "llh", 0x95, INSTR_RXY_RRRD },
+ { "cgh", 0x34, INSTR_RXY_RRRD },
+ { "laey", 0x75, INSTR_RXY_RRRD },
+ { "ltgf", 0x32, INSTR_RXY_RRRD },
+ { "mfy", 0x5c, INSTR_RXY_RRRD },
+ { "mhy", 0x7c, INSTR_RXY_RRRD },
+ { "pfd", 0x36, INSTR_RXY_URRD },
+ { "lbh", 0xc0, INSTR_RXY_RRRD },
+ { "llch", 0xc2, INSTR_RXY_RRRD },
+ { "stch", 0xc3, INSTR_RXY_RRRD },
+ { "lhh", 0xc4, INSTR_RXY_RRRD },
+ { "llhh", 0xc6, INSTR_RXY_RRRD },
+ { "sthh", 0xc7, INSTR_RXY_RRRD },
+ { "lfh", 0xca, INSTR_RXY_RRRD },
+ { "stfh", 0xcb, INSTR_RXY_RRRD },
+ { "chf", 0xcd, INSTR_RXY_RRRD },
+ { "clhf", 0xcf, INSTR_RXY_RRRD },
+#endif
+ { "lrv", 0x1e, INSTR_RXY_RRRD },
+ { "lrvh", 0x1f, INSTR_RXY_RRRD },
+ { "strv", 0x3e, INSTR_RXY_RRRD },
+ { "ml", 0x96, INSTR_RXY_RRRD },
+ { "dl", 0x97, INSTR_RXY_RRRD },
+ { "alc", 0x98, INSTR_RXY_RRRD },
+ { "slb", 0x99, INSTR_RXY_RRRD },
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_e5[] = {
+#ifdef CONFIG_64BIT
+ { "strag", 0x02, INSTR_SSE_RDRD },
+ { "chhsi", 0x54, INSTR_SIL_RDI },
+ { "chsi", 0x5c, INSTR_SIL_RDI },
+ { "cghsi", 0x58, INSTR_SIL_RDI },
+ { { 0, LONG_INSN_CLHHSI }, 0x55, INSTR_SIL_RDU },
+ { { 0, LONG_INSN_CLFHSI }, 0x5d, INSTR_SIL_RDU },
+ { { 0, LONG_INSN_CLGHSI }, 0x59, INSTR_SIL_RDU },
+ { "mvhhi", 0x44, INSTR_SIL_RDI },
+ { "mvhi", 0x4c, INSTR_SIL_RDI },
+ { "mvghi", 0x48, INSTR_SIL_RDI },
+#endif
+ { "lasp", 0x00, INSTR_SSE_RDRD },
+ { "tprot", 0x01, INSTR_SSE_RDRD },
+ { "mvcsk", 0x0e, INSTR_SSE_RDRD },
+ { "mvcdk", 0x0f, INSTR_SSE_RDRD },
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_eb[] = {
+#ifdef CONFIG_64BIT
+ { "lmg", 0x04, INSTR_RSY_RRRD },
+ { "srag", 0x0a, INSTR_RSY_RRRD },
+ { "slag", 0x0b, INSTR_RSY_RRRD },
+ { "srlg", 0x0c, INSTR_RSY_RRRD },
+ { "sllg", 0x0d, INSTR_RSY_RRRD },
+ { "tracg", 0x0f, INSTR_RSY_RRRD },
+ { "csy", 0x14, INSTR_RSY_RRRD },
+ { "rllg", 0x1c, INSTR_RSY_RRRD },
+ { "clmh", 0x20, INSTR_RSY_RURD },
+ { "clmy", 0x21, INSTR_RSY_RURD },
+ { "stmg", 0x24, INSTR_RSY_RRRD },
+ { "stctg", 0x25, INSTR_RSY_CCRD },
+ { "stmh", 0x26, INSTR_RSY_RRRD },
+ { "stcmh", 0x2c, INSTR_RSY_RURD },
+ { "stcmy", 0x2d, INSTR_RSY_RURD },
+ { "lctlg", 0x2f, INSTR_RSY_CCRD },
+ { "csg", 0x30, INSTR_RSY_RRRD },
+ { "cdsy", 0x31, INSTR_RSY_RRRD },
+ { "cdsg", 0x3e, INSTR_RSY_RRRD },
+ { "bxhg", 0x44, INSTR_RSY_RRRD },
+ { "bxleg", 0x45, INSTR_RSY_RRRD },
+ { "tmy", 0x51, INSTR_SIY_URD },
+ { "mviy", 0x52, INSTR_SIY_URD },
+ { "niy", 0x54, INSTR_SIY_URD },
+ { "cliy", 0x55, INSTR_SIY_URD },
+ { "oiy", 0x56, INSTR_SIY_URD },
+ { "xiy", 0x57, INSTR_SIY_URD },
+ { "icmh", 0x80, INSTR_RSE_RURD },
+ { "icmh", 0x80, INSTR_RSY_RURD },
+ { "icmy", 0x81, INSTR_RSY_RURD },
+ { "clclu", 0x8f, INSTR_RSY_RRRD },
+ { "stmy", 0x90, INSTR_RSY_RRRD },
+ { "lmh", 0x96, INSTR_RSY_RRRD },
+ { "lmy", 0x98, INSTR_RSY_RRRD },
+ { "lamy", 0x9a, INSTR_RSY_AARD },
+ { "stamy", 0x9b, INSTR_RSY_AARD },
+ { "asi", 0x6a, INSTR_SIY_IRD },
+ { "agsi", 0x7a, INSTR_SIY_IRD },
+ { "alsi", 0x6e, INSTR_SIY_IRD },
+ { "algsi", 0x7e, INSTR_SIY_IRD },
+ { "ecag", 0x4c, INSTR_RSY_RRRD },
+ { "srak", 0xdc, INSTR_RSY_RRRD },
+ { "slak", 0xdd, INSTR_RSY_RRRD },
+ { "srlk", 0xde, INSTR_RSY_RRRD },
+ { "sllk", 0xdf, INSTR_RSY_RRRD },
+ { "locg", 0xe2, INSTR_RSY_RDRM },
+ { "stocg", 0xe3, INSTR_RSY_RDRM },
+ { "lang", 0xe4, INSTR_RSY_RRRD },
+ { "laog", 0xe6, INSTR_RSY_RRRD },
+ { "laxg", 0xe7, INSTR_RSY_RRRD },
+ { "laag", 0xe8, INSTR_RSY_RRRD },
+ { "laalg", 0xea, INSTR_RSY_RRRD },
+ { "loc", 0xf2, INSTR_RSY_RDRM },
+ { "stoc", 0xf3, INSTR_RSY_RDRM },
+ { "lan", 0xf4, INSTR_RSY_RRRD },
+ { "lao", 0xf6, INSTR_RSY_RRRD },
+ { "lax", 0xf7, INSTR_RSY_RRRD },
+ { "laa", 0xf8, INSTR_RSY_RRRD },
+ { "laal", 0xfa, INSTR_RSY_RRRD },
+#endif
+ { "rll", 0x1d, INSTR_RSY_RRRD },
+ { "mvclu", 0x8e, INSTR_RSY_RRRD },
+ { "tp", 0xc0, INSTR_RSL_R0RD },
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_ec[] = {
+#ifdef CONFIG_64BIT
+ { "brxhg", 0x44, INSTR_RIE_RRP },
+ { "brxlg", 0x45, INSTR_RIE_RRP },
+ { "crb", 0xf6, INSTR_RRS_RRRDU },
+ { "cgrb", 0xe4, INSTR_RRS_RRRDU },
+ { "crj", 0x76, INSTR_RIE_RRPU },
+ { "cgrj", 0x64, INSTR_RIE_RRPU },
+ { "cib", 0xfe, INSTR_RIS_RURDI },
+ { "cgib", 0xfc, INSTR_RIS_RURDI },
+ { "cij", 0x7e, INSTR_RIE_RUPI },
+ { "cgij", 0x7c, INSTR_RIE_RUPI },
+ { "cit", 0x72, INSTR_RIE_R0IU },
+ { "cgit", 0x70, INSTR_RIE_R0IU },
+ { "clrb", 0xf7, INSTR_RRS_RRRDU },
+ { "clgrb", 0xe5, INSTR_RRS_RRRDU },
+ { "clrj", 0x77, INSTR_RIE_RRPU },
+ { "clgrj", 0x65, INSTR_RIE_RRPU },
+ { "clib", 0xff, INSTR_RIS_RURDU },
+ { "clgib", 0xfd, INSTR_RIS_RURDU },
+ { "clij", 0x7f, INSTR_RIE_RUPU },
+ { "clgij", 0x7d, INSTR_RIE_RUPU },
+ { "clfit", 0x73, INSTR_RIE_R0UU },
+ { "clgit", 0x71, INSTR_RIE_R0UU },
+ { "rnsbg", 0x54, INSTR_RIE_RRUUU },
+ { "rxsbg", 0x57, INSTR_RIE_RRUUU },
+ { "rosbg", 0x56, INSTR_RIE_RRUUU },
+ { "risbg", 0x55, INSTR_RIE_RRUUU },
+ { { 0, LONG_INSN_RISBLG }, 0x51, INSTR_RIE_RRUUU },
+ { { 0, LONG_INSN_RISBHG }, 0x5D, INSTR_RIE_RRUUU },
+ { "ahik", 0xd8, INSTR_RIE_RRI0 },
+ { "aghik", 0xd9, INSTR_RIE_RRI0 },
+ { { 0, LONG_INSN_ALHSIK }, 0xda, INSTR_RIE_RRI0 },
+ { { 0, LONG_INSN_ALGHSIK }, 0xdb, INSTR_RIE_RRI0 },
+#endif
+ { "", 0, INSTR_INVALID }
+};
+
+static struct insn opcode_ed[] = {
+#ifdef CONFIG_64BIT
+ { "mayl", 0x38, INSTR_RXF_FRRDF },
+ { "myl", 0x39, INSTR_RXF_FRRDF },
+ { "may", 0x3a, INSTR_RXF_FRRDF },
+ { "my", 0x3b, INSTR_RXF_FRRDF },
+ { "mayh", 0x3c, INSTR_RXF_FRRDF },
+ { "myh", 0x3d, INSTR_RXF_FRRDF },
+ { "ley", 0x64, INSTR_RXY_FRRD },
+ { "ldy", 0x65, INSTR_RXY_FRRD },
+ { "stey", 0x66, INSTR_RXY_FRRD },
+ { "stdy", 0x67, INSTR_RXY_FRRD },
+ { "sldt", 0x40, INSTR_RXF_FRRDF },
+ { "slxt", 0x48, INSTR_RXF_FRRDF },
+ { "srdt", 0x41, INSTR_RXF_FRRDF },
+ { "srxt", 0x49, INSTR_RXF_FRRDF },
+ { "tdcet", 0x50, INSTR_RXE_FRRD },
+ { "tdcdt", 0x54, INSTR_RXE_FRRD },
+ { "tdcxt", 0x58, INSTR_RXE_FRRD },
+ { "tdget", 0x51, INSTR_RXE_FRRD },
+ { "tdgdt", 0x55, INSTR_RXE_FRRD },
+ { "tdgxt", 0x59, INSTR_RXE_FRRD },
+#endif
+ { "ldeb", 0x04, INSTR_RXE_FRRD },
+ { "lxdb", 0x05, INSTR_RXE_FRRD },
+ { "lxeb", 0x06, INSTR_RXE_FRRD },
+ { "mxdb", 0x07, INSTR_RXE_FRRD },
+ { "keb", 0x08, INSTR_RXE_FRRD },
+ { "ceb", 0x09, INSTR_RXE_FRRD },
+ { "aeb", 0x0a, INSTR_RXE_FRRD },
+ { "seb", 0x0b, INSTR_RXE_FRRD },
+ { "mdeb", 0x0c, INSTR_RXE_FRRD },
+ { "deb", 0x0d, INSTR_RXE_FRRD },
+ { "maeb", 0x0e, INSTR_RXF_FRRDF },
+ { "mseb", 0x0f, INSTR_RXF_FRRDF },
+ { "tceb", 0x10, INSTR_RXE_FRRD },
+ { "tcdb", 0x11, INSTR_RXE_FRRD },
+ { "tcxb", 0x12, INSTR_RXE_FRRD },
+ { "sqeb", 0x14, INSTR_RXE_FRRD },
+ { "sqdb", 0x15, INSTR_RXE_FRRD },
+ { "meeb", 0x17, INSTR_RXE_FRRD },
+ { "kdb", 0x18, INSTR_RXE_FRRD },
+ { "cdb", 0x19, INSTR_RXE_FRRD },
+ { "adb", 0x1a, INSTR_RXE_FRRD },
+ { "sdb", 0x1b, INSTR_RXE_FRRD },
+ { "mdb", 0x1c, INSTR_RXE_FRRD },
+ { "ddb", 0x1d, INSTR_RXE_FRRD },
+ { "madb", 0x1e, INSTR_RXF_FRRDF },
+ { "msdb", 0x1f, INSTR_RXF_FRRDF },
+ { "lde", 0x24, INSTR_RXE_FRRD },
+ { "lxd", 0x25, INSTR_RXE_FRRD },
+ { "lxe", 0x26, INSTR_RXE_FRRD },
+ { "mae", 0x2e, INSTR_RXF_FRRDF },
+ { "mse", 0x2f, INSTR_RXF_FRRDF },
+ { "sqe", 0x34, INSTR_RXE_FRRD },
+ { "sqd", 0x35, INSTR_RXE_FRRD },
+ { "mee", 0x37, INSTR_RXE_FRRD },
+ { "mad", 0x3e, INSTR_RXF_FRRDF },
+ { "msd", 0x3f, INSTR_RXF_FRRDF },
+ { "", 0, INSTR_INVALID }
+};
+
+/* Extracts an operand value from an instruction. */
+static unsigned int extract_operand(unsigned char *code,
+ const struct operand *operand)
+{
+ unsigned int val;
+ int bits;
+
+ /* Extract fragments of the operand byte for byte. */
+ code += operand->shift / 8;
+ bits = (operand->shift & 7) + operand->bits;
+ val = 0;
+ do {
+ val <<= 8;
+ val |= (unsigned int) *code++;
+ bits -= 8;
+ } while (bits > 0);
+ val >>= -bits;
+ val &= ((1U << (operand->bits - 1)) << 1) - 1;
+
+ /* Check for special long displacement case. */
+ if (operand->bits == 20 && operand->shift == 20)
+ val = (val & 0xff) << 12 | (val & 0xfff00) >> 8;
+
+ /* Sign extend value if the operand is signed or pc relative. */
+ if ((operand->flags & (OPERAND_SIGNED | OPERAND_PCREL)) &&
+ (val & (1U << (operand->bits - 1))))
+ val |= (-1U << (operand->bits - 1)) << 1;
+
+ /* Double value if the operand is pc relative. */
+ if (operand->flags & OPERAND_PCREL)
+ val <<= 1;
+
+ /* Length x in an instructions has real length x + 1. */
+ if (operand->flags & OPERAND_LENGTH)
+ val++;
+ return val;
+}
+
+static inline int insn_length(unsigned char code)
+{
+ return ((((int) code + 64) >> 7) + 1) << 1;
+}
+
+static struct insn *find_insn(unsigned char *code)
+{
+ unsigned char opfrag = code[1];
+ unsigned char opmask;
+ struct insn *table;
+
+ switch (code[0]) {
+ case 0x01:
+ table = opcode_01;
+ break;
+ case 0xa5:
+ table = opcode_a5;
+ break;
+ case 0xa7:
+ table = opcode_a7;
+ break;
+ case 0xb2:
+ table = opcode_b2;
+ break;
+ case 0xb3:
+ table = opcode_b3;
+ break;
+ case 0xb9:
+ table = opcode_b9;
+ break;
+ case 0xc0:
+ table = opcode_c0;
+ break;
+ case 0xc2:
+ table = opcode_c2;
+ break;
+ case 0xc4:
+ table = opcode_c4;
+ break;
+ case 0xc6:
+ table = opcode_c6;
+ break;
+ case 0xc8:
+ table = opcode_c8;
+ break;
+ case 0xcc:
+ table = opcode_cc;
+ break;
+ case 0xe3:
+ table = opcode_e3;
+ opfrag = code[5];
+ break;
+ case 0xe5:
+ table = opcode_e5;
+ break;
+ case 0xeb:
+ table = opcode_eb;
+ opfrag = code[5];
+ break;
+ case 0xec:
+ table = opcode_ec;
+ opfrag = code[5];
+ break;
+ case 0xed:
+ table = opcode_ed;
+ opfrag = code[5];
+ break;
+ default:
+ table = opcode;
+ opfrag = code[0];
+ break;
+ }
+ while (table->format != INSTR_INVALID) {
+ opmask = formats[table->format][0];
+ if (table->opfrag == (opfrag & opmask))
+ return table;
+ table++;
+ }
+ return NULL;
+}
+
+static int print_insn(char *buffer, unsigned char *code, unsigned long addr)
+{
+ struct insn *insn;
+ const unsigned char *ops;
+ const struct operand *operand;
+ unsigned int value;
+ char separator;
+ char *ptr;
+ int i;
+
+ ptr = buffer;
+ insn = find_insn(code);
+ if (insn) {
+ if (insn->name[0] == '\0')
+ ptr += sprintf(ptr, "%s\t",
+ long_insn_name[(int) insn->name[1]]);
+ else
+ ptr += sprintf(ptr, "%.5s\t", insn->name);
+ /* Extract the operands. */
+ separator = 0;
+ for (ops = formats[insn->format] + 1, i = 0;
+ *ops != 0 && i < 6; ops++, i++) {
+ operand = operands + *ops;
+ value = extract_operand(code, operand);
+ if ((operand->flags & OPERAND_INDEX) && value == 0)
+ continue;
+ if ((operand->flags & OPERAND_BASE) &&
+ value == 0 && separator == '(') {
+ separator = ',';
+ continue;
+ }
+ if (separator)
+ ptr += sprintf(ptr, "%c", separator);
+ if (operand->flags & OPERAND_GPR)
+ ptr += sprintf(ptr, "%%r%i", value);
+ else if (operand->flags & OPERAND_FPR)
+ ptr += sprintf(ptr, "%%f%i", value);
+ else if (operand->flags & OPERAND_AR)
+ ptr += sprintf(ptr, "%%a%i", value);
+ else if (operand->flags & OPERAND_CR)
+ ptr += sprintf(ptr, "%%c%i", value);
+ else if (operand->flags & OPERAND_PCREL)
+ ptr += sprintf(ptr, "%lx", (signed int) value
+ + addr);
+ else if (operand->flags & OPERAND_SIGNED)
+ ptr += sprintf(ptr, "%i", value);
+ else
+ ptr += sprintf(ptr, "%u", value);
+ if (operand->flags & OPERAND_DISP)
+ separator = '(';
+ else if (operand->flags & OPERAND_BASE) {
+ ptr += sprintf(ptr, ")");
+ separator = ',';
+ } else
+ separator = ',';
+ }
+ } else
+ ptr += sprintf(ptr, "unknown");
+ return (int) (ptr - buffer);
+}
+
+void show_code(struct pt_regs *regs)
+{
+ char *mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl";
+ unsigned char code[64];
+ char buffer[64], *ptr;
+ mm_segment_t old_fs;
+ unsigned long addr;
+ int start, end, opsize, hops, i;
+
+ /* Get a snapshot of the 64 bytes surrounding the fault address. */
+ old_fs = get_fs();
+ set_fs((regs->psw.mask & PSW_MASK_PSTATE) ? USER_DS : KERNEL_DS);
+ for (start = 32; start && regs->psw.addr >= 34 - start; start -= 2) {
+ addr = regs->psw.addr - 34 + start;
+ if (__copy_from_user(code + start - 2,
+ (char __user *) addr, 2))
+ break;
+ }
+ for (end = 32; end < 64; end += 2) {
+ addr = regs->psw.addr + end - 32;
+ if (__copy_from_user(code + end,
+ (char __user *) addr, 2))
+ break;
+ }
+ set_fs(old_fs);
+ /* Code snapshot useable ? */
+ if ((regs->psw.addr & 1) || start >= end) {
+ printk("%s Code: Bad PSW.\n", mode);
+ return;
+ }
+ /* Find a starting point for the disassembly. */
+ while (start < 32) {
+ for (i = 0, hops = 0; start + i < 32 && hops < 3; hops++) {
+ if (!find_insn(code + start + i))
+ break;
+ i += insn_length(code[start + i]);
+ }
+ if (start + i == 32)
+ /* Looks good, sequence ends at PSW. */
+ break;
+ start += 2;
+ }
+ /* Decode the instructions. */
+ ptr = buffer;
+ ptr += sprintf(ptr, "%s Code:", mode);
+ hops = 0;
+ while (start < end && hops < 8) {
+ opsize = insn_length(code[start]);
+ if (start + opsize == 32)
+ *ptr++ = '#';
+ else if (start == 32)
+ *ptr++ = '>';
+ else
+ *ptr++ = ' ';
+ addr = regs->psw.addr + start - 32;
+ ptr += sprintf(ptr, ONELONG, addr);
+ if (start + opsize >= end)
+ break;
+ for (i = 0; i < opsize; i++)
+ ptr += sprintf(ptr, "%02x", code[start + i]);
+ *ptr++ = '\t';
+ if (i < 6)
+ *ptr++ = '\t';
+ ptr += print_insn(ptr, code + start, addr);
+ start += opsize;
+ printk(buffer);
+ ptr = buffer;
+ ptr += sprintf(ptr, "\n ");
+ hops++;
+ }
+ printk("\n");
+}
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
new file mode 100644
index 00000000..9475e682
--- /dev/null
+++ b/arch/s390/kernel/early.c
@@ -0,0 +1,482 @@
+/*
+ * arch/s390/kernel/early.c
+ *
+ * Copyright IBM Corp. 2007, 2009
+ * Author(s): Hongjie Yang <hongjie@us.ibm.com>,
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#define KMSG_COMPONENT "setup"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/compiler.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/ftrace.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/pfn.h>
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <asm/ebcdic.h>
+#include <asm/ipl.h>
+#include <asm/lowcore.h>
+#include <asm/processor.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/sysinfo.h>
+#include <asm/cpcmd.h>
+#include <asm/sclp.h>
+#include <asm/facility.h>
+#include "entry.h"
+
+/*
+ * Create a Kernel NSS if the SAVESYS= parameter is defined
+ */
+#define DEFSYS_CMD_SIZE 128
+#define SAVESYS_CMD_SIZE 32
+
+char kernel_nss_name[NSS_NAME_SIZE + 1];
+
+static void __init setup_boot_command_line(void);
+
+/*
+ * Get the TOD clock running.
+ */
+static void __init reset_tod_clock(void)
+{
+ u64 time;
+
+ if (store_clock(&time) == 0)
+ return;
+ /* TOD clock not running. Set the clock to Unix Epoch. */
+ if (set_clock(TOD_UNIX_EPOCH) != 0 || store_clock(&time) != 0)
+ disabled_wait(0);
+
+ sched_clock_base_cc = TOD_UNIX_EPOCH;
+ S390_lowcore.last_update_clock = sched_clock_base_cc;
+}
+
+#ifdef CONFIG_SHARED_KERNEL
+int __init savesys_ipl_nss(char *cmd, const int cmdlen);
+
+asm(
+ " .section .init.text,\"ax\",@progbits\n"
+ " .align 4\n"
+ " .type savesys_ipl_nss, @function\n"
+ "savesys_ipl_nss:\n"
+#ifdef CONFIG_64BIT
+ " stmg 6,15,48(15)\n"
+ " lgr 14,3\n"
+ " sam31\n"
+ " diag 2,14,0x8\n"
+ " sam64\n"
+ " lgr 2,14\n"
+ " lmg 6,15,48(15)\n"
+#else
+ " stm 6,15,24(15)\n"
+ " lr 14,3\n"
+ " diag 2,14,0x8\n"
+ " lr 2,14\n"
+ " lm 6,15,24(15)\n"
+#endif
+ " br 14\n"
+ " .size savesys_ipl_nss, .-savesys_ipl_nss\n"
+ " .previous\n");
+
+static __initdata char upper_command_line[COMMAND_LINE_SIZE];
+
+static noinline __init void create_kernel_nss(void)
+{
+ unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size;
+#ifdef CONFIG_BLK_DEV_INITRD
+ unsigned int sinitrd_pfn, einitrd_pfn;
+#endif
+ int response;
+ int hlen;
+ size_t len;
+ char *savesys_ptr;
+ char defsys_cmd[DEFSYS_CMD_SIZE];
+ char savesys_cmd[SAVESYS_CMD_SIZE];
+
+ /* Do nothing if we are not running under VM */
+ if (!MACHINE_IS_VM)
+ return;
+
+ /* Convert COMMAND_LINE to upper case */
+ for (i = 0; i < strlen(boot_command_line); i++)
+ upper_command_line[i] = toupper(boot_command_line[i]);
+
+ savesys_ptr = strstr(upper_command_line, "SAVESYS=");
+
+ if (!savesys_ptr)
+ return;
+
+ savesys_ptr += 8; /* Point to the beginning of the NSS name */
+ for (i = 0; i < NSS_NAME_SIZE; i++) {
+ if (savesys_ptr[i] == ' ' || savesys_ptr[i] == '\0')
+ break;
+ kernel_nss_name[i] = savesys_ptr[i];
+ }
+
+ stext_pfn = PFN_DOWN(__pa(&_stext));
+ eshared_pfn = PFN_DOWN(__pa(&_eshared));
+ end_pfn = PFN_UP(__pa(&_end));
+ min_size = end_pfn << 2;
+
+ hlen = snprintf(defsys_cmd, DEFSYS_CMD_SIZE,
+ "DEFSYS %s 00000-%.5X EW %.5X-%.5X SR %.5X-%.5X",
+ kernel_nss_name, stext_pfn - 1, stext_pfn,
+ eshared_pfn - 1, eshared_pfn, end_pfn);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (INITRD_START && INITRD_SIZE) {
+ sinitrd_pfn = PFN_DOWN(__pa(INITRD_START));
+ einitrd_pfn = PFN_UP(__pa(INITRD_START + INITRD_SIZE));
+ min_size = einitrd_pfn << 2;
+ hlen += snprintf(defsys_cmd + hlen, DEFSYS_CMD_SIZE - hlen,
+ " EW %.5X-%.5X", sinitrd_pfn, einitrd_pfn);
+ }
+#endif
+
+ snprintf(defsys_cmd + hlen, DEFSYS_CMD_SIZE - hlen,
+ " EW MINSIZE=%.7iK PARMREGS=0-13", min_size);
+ defsys_cmd[DEFSYS_CMD_SIZE - 1] = '\0';
+ snprintf(savesys_cmd, SAVESYS_CMD_SIZE, "SAVESYS %s \n IPL %s",
+ kernel_nss_name, kernel_nss_name);
+ savesys_cmd[SAVESYS_CMD_SIZE - 1] = '\0';
+
+ __cpcmd(defsys_cmd, NULL, 0, &response);
+
+ if (response != 0) {
+ pr_err("Defining the Linux kernel NSS failed with rc=%d\n",
+ response);
+ kernel_nss_name[0] = '\0';
+ return;
+ }
+
+ len = strlen(savesys_cmd);
+ ASCEBC(savesys_cmd, len);
+ response = savesys_ipl_nss(savesys_cmd, len);
+
+ /* On success: response is equal to the command size,
+ * max SAVESYS_CMD_SIZE
+ * On error: response contains the numeric portion of cp error message.
+ * for SAVESYS it will be >= 263
+ * for missing privilege class, it will be 1
+ */
+ if (response > SAVESYS_CMD_SIZE || response == 1) {
+ pr_err("Saving the Linux kernel NSS failed with rc=%d\n",
+ response);
+ kernel_nss_name[0] = '\0';
+ return;
+ }
+
+ /* re-initialize cputime accounting. */
+ sched_clock_base_cc = get_clock();
+ S390_lowcore.last_update_clock = sched_clock_base_cc;
+ S390_lowcore.last_update_timer = 0x7fffffffffffffffULL;
+ S390_lowcore.user_timer = 0;
+ S390_lowcore.system_timer = 0;
+ asm volatile("SPT 0(%0)" : : "a" (&S390_lowcore.last_update_timer));
+
+ /* re-setup boot command line with new ipl vm parms */
+ ipl_update_parameters();
+ setup_boot_command_line();
+
+ ipl_flags = IPL_NSS_VALID;
+}
+
+#else /* CONFIG_SHARED_KERNEL */
+
+static inline void create_kernel_nss(void) { }
+
+#endif /* CONFIG_SHARED_KERNEL */
+
+/*
+ * Clear bss memory
+ */
+static noinline __init void clear_bss_section(void)
+{
+ memset(__bss_start, 0, __bss_stop - __bss_start);
+}
+
+/*
+ * Initialize storage key for kernel pages
+ */
+static noinline __init void init_kernel_storage_key(void)
+{
+ unsigned long end_pfn, init_pfn;
+
+ end_pfn = PFN_UP(__pa(&_end));
+
+ for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++)
+ page_set_storage_key(init_pfn << PAGE_SHIFT,
+ PAGE_DEFAULT_KEY, 0);
+}
+
+static __initdata struct sysinfo_3_2_2 vmms __aligned(PAGE_SIZE);
+
+static noinline __init void detect_machine_type(void)
+{
+ /* Check current-configuration-level */
+ if ((stsi(NULL, 0, 0, 0) >> 28) <= 2) {
+ S390_lowcore.machine_flags |= MACHINE_FLAG_LPAR;
+ return;
+ }
+ /* Get virtual-machine cpu information. */
+ if (stsi(&vmms, 3, 2, 2) == -ENOSYS || !vmms.count)
+ return;
+
+ /* Running under KVM? If not we assume z/VM */
+ if (!memcmp(vmms.vm[0].cpi, "\xd2\xe5\xd4", 3))
+ S390_lowcore.machine_flags |= MACHINE_FLAG_KVM;
+ else
+ S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
+}
+
+static __init void early_pgm_check_handler(void)
+{
+ unsigned long addr;
+ const struct exception_table_entry *fixup;
+
+ addr = S390_lowcore.program_old_psw.addr;
+ fixup = search_exception_tables(addr & PSW_ADDR_INSN);
+ if (!fixup)
+ disabled_wait(0);
+ S390_lowcore.program_old_psw.addr = fixup->fixup | PSW_ADDR_AMODE;
+}
+
+static noinline __init void setup_lowcore_early(void)
+{
+ psw_t psw;
+
+ psw.mask = PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA;
+ psw.addr = PSW_ADDR_AMODE | (unsigned long) s390_base_ext_handler;
+ S390_lowcore.external_new_psw = psw;
+ psw.addr = PSW_ADDR_AMODE | (unsigned long) s390_base_pgm_handler;
+ S390_lowcore.program_new_psw = psw;
+ s390_base_pgm_handler_fn = early_pgm_check_handler;
+}
+
+static noinline __init void setup_facility_list(void)
+{
+ stfle(S390_lowcore.stfle_fac_list,
+ ARRAY_SIZE(S390_lowcore.stfle_fac_list));
+}
+
+static noinline __init void setup_hpage(void)
+{
+#ifndef CONFIG_DEBUG_PAGEALLOC
+ if (!test_facility(2) || !test_facility(8))
+ return;
+ S390_lowcore.machine_flags |= MACHINE_FLAG_HPAGE;
+ __ctl_set_bit(0, 23);
+#endif
+}
+
+static __init void detect_mvpg(void)
+{
+#ifndef CONFIG_64BIT
+ int rc;
+
+ asm volatile(
+ " la 0,0\n"
+ " mvpg %2,%2\n"
+ "0: la %0,0\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "=d" (rc) : "0" (-EOPNOTSUPP), "a" (0) : "memory", "cc", "0");
+ if (!rc)
+ S390_lowcore.machine_flags |= MACHINE_FLAG_MVPG;
+#endif
+}
+
+static __init void detect_ieee(void)
+{
+#ifndef CONFIG_64BIT
+ int rc, tmp;
+
+ asm volatile(
+ " efpc %1,0\n"
+ "0: la %0,0\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "=d" (rc), "=d" (tmp): "0" (-EOPNOTSUPP) : "cc");
+ if (!rc)
+ S390_lowcore.machine_flags |= MACHINE_FLAG_IEEE;
+#endif
+}
+
+static __init void detect_csp(void)
+{
+#ifndef CONFIG_64BIT
+ int rc;
+
+ asm volatile(
+ " la 0,0\n"
+ " la 1,0\n"
+ " la 2,4\n"
+ " csp 0,2\n"
+ "0: la %0,0\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "=d" (rc) : "0" (-EOPNOTSUPP) : "cc", "0", "1", "2");
+ if (!rc)
+ S390_lowcore.machine_flags |= MACHINE_FLAG_CSP;
+#endif
+}
+
+static __init void detect_diag9c(void)
+{
+ unsigned int cpu_address;
+ int rc;
+
+ cpu_address = stap();
+ asm volatile(
+ " diag %2,0,0x9c\n"
+ "0: la %0,0\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "=d" (rc) : "0" (-EOPNOTSUPP), "d" (cpu_address) : "cc");
+ if (!rc)
+ S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG9C;
+}
+
+static __init void detect_diag44(void)
+{
+#ifdef CONFIG_64BIT
+ int rc;
+
+ asm volatile(
+ " diag 0,0,0x44\n"
+ "0: la %0,0\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "=d" (rc) : "0" (-EOPNOTSUPP) : "cc");
+ if (!rc)
+ S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG44;
+#endif
+}
+
+static __init void detect_machine_facilities(void)
+{
+#ifdef CONFIG_64BIT
+ if (test_facility(3))
+ S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;
+ if (test_facility(8))
+ S390_lowcore.machine_flags |= MACHINE_FLAG_PFMF;
+ if (test_facility(11))
+ S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY;
+ if (test_facility(27))
+ S390_lowcore.machine_flags |= MACHINE_FLAG_MVCOS;
+ if (test_facility(40))
+ S390_lowcore.machine_flags |= MACHINE_FLAG_SPP;
+ if (test_facility(25))
+ S390_lowcore.machine_flags |= MACHINE_FLAG_STCKF;
+#endif
+}
+
+static __init void rescue_initrd(void)
+{
+#ifdef CONFIG_BLK_DEV_INITRD
+ unsigned long min_initrd_addr = (unsigned long) _end + (4UL << 20);
+ /*
+ * Just like in case of IPL from VM reader we make sure there is a
+ * gap of 4MB between end of kernel and start of initrd.
+ * That way we can also be sure that saving an NSS will succeed,
+ * which however only requires different segments.
+ */
+ if (!INITRD_START || !INITRD_SIZE)
+ return;
+ if (INITRD_START >= min_initrd_addr)
+ return;
+ memmove((void *) min_initrd_addr, (void *) INITRD_START, INITRD_SIZE);
+ INITRD_START = min_initrd_addr;
+#endif
+}
+
+/* Set up boot command line */
+static void __init append_to_cmdline(size_t (*ipl_data)(char *, size_t))
+{
+ char *parm, *delim;
+ size_t rc, len;
+
+ len = strlen(boot_command_line);
+
+ delim = boot_command_line + len; /* '\0' character position */
+ parm = boot_command_line + len + 1; /* append right after '\0' */
+
+ rc = ipl_data(parm, COMMAND_LINE_SIZE - len - 1);
+ if (rc) {
+ if (*parm == '=')
+ memmove(boot_command_line, parm + 1, rc);
+ else
+ *delim = ' '; /* replace '\0' with space */
+ }
+}
+
+static inline int has_ebcdic_char(const char *str)
+{
+ int i;
+
+ for (i = 0; str[i]; i++)
+ if (str[i] & 0x80)
+ return 1;
+ return 0;
+}
+
+static void __init setup_boot_command_line(void)
+{
+ COMMAND_LINE[ARCH_COMMAND_LINE_SIZE - 1] = 0;
+ /* convert arch command line to ascii if necessary */
+ if (has_ebcdic_char(COMMAND_LINE))
+ EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE);
+ /* copy arch command line */
+ strlcpy(boot_command_line, strstrip(COMMAND_LINE),
+ ARCH_COMMAND_LINE_SIZE);
+
+ /* append IPL PARM data to the boot command line */
+ if (MACHINE_IS_VM)
+ append_to_cmdline(append_ipl_vmparm);
+
+ append_to_cmdline(append_ipl_scpdata);
+}
+
+
+/*
+ * Save ipl parameters, clear bss memory, initialize storage keys
+ * and create a kernel NSS at startup if the SAVESYS= parm is defined
+ */
+void __init startup_init(void)
+{
+ reset_tod_clock();
+ ipl_save_parameters();
+ rescue_initrd();
+ clear_bss_section();
+ init_kernel_storage_key();
+ lockdep_init();
+ lockdep_off();
+ sort_main_extable();
+ setup_lowcore_early();
+ setup_facility_list();
+ detect_machine_type();
+ ipl_update_parameters();
+ setup_boot_command_line();
+ create_kernel_nss();
+ detect_mvpg();
+ detect_ieee();
+ detect_csp();
+ detect_diag9c();
+ detect_diag44();
+ detect_machine_facilities();
+ setup_hpage();
+ sclp_facilities_detect();
+ detect_memory_layout(memory_chunk);
+#ifdef CONFIG_DYNAMIC_FTRACE
+ S390_lowcore.ftrace_func = (unsigned long)ftrace_caller;
+#endif
+ lockdep_on();
+}
diff --git a/arch/s390/kernel/ebcdic.c b/arch/s390/kernel/ebcdic.c
new file mode 100644
index 00000000..cc0dc609
--- /dev/null
+++ b/arch/s390/kernel/ebcdic.c
@@ -0,0 +1,401 @@
+/*
+ * arch/s390/kernel/ebcdic.c
+ * ECBDIC -> ASCII, ASCII -> ECBDIC,
+ * upper to lower case (EBCDIC) conversion tables.
+ *
+ * S390 version
+ * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Martin Peschke <peschke@fh-brandenburg.de>
+ */
+
+#include <linux/module.h>
+#include <asm/types.h>
+#include <asm/ebcdic.h>
+
+/*
+ * ASCII (IBM PC 437) -> EBCDIC 037
+ */
+__u8 _ascebc[256] =
+{
+ /*00 NUL SOH STX ETX EOT ENQ ACK BEL */
+ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F,
+ /*08 BS HT LF VT FF CR SO SI */
+ /* ->NL */
+ 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ /*10 DLE DC1 DC2 DC3 DC4 NAK SYN ETB */
+ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26,
+ /*18 CAN EM SUB ESC FS GS RS US */
+ /* ->IGS ->IRS ->IUS */
+ 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F,
+ /*20 SP ! " # $ % & ' */
+ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D,
+ /*28 ( ) * + , - . / */
+ 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
+ /*30 0 1 2 3 4 5 6 7 */
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ /*38 8 9 : ; < = > ? */
+ 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
+ /*40 @ A B C D E F G */
+ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ /*48 H I J K L M N O */
+ 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
+ /*50 P Q R S T U V W */
+ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
+ /*58 X Y Z [ \ ] ^ _ */
+ 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D,
+ /*60 ` a b c d e f g */
+ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ /*68 h i j k l m n o */
+ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
+ /*70 p q r s t u v w */
+ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
+ /*78 x y z { | } ~ DL */
+ 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07,
+ /*80*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*88*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*90*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*98*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*A0*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*A8*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*B0*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*B8*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*C0*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*C8*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*D0*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*D8*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*E0 sz */
+ 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*E8*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*F0*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*F8*/
+ 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF
+};
+
+/*
+ * EBCDIC 037 -> ASCII (IBM PC 437)
+ */
+__u8 _ebcasc[256] =
+{
+ /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
+ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
+ /* 0x08 -GE -SPS -RPT VT FF CR SO SI */
+ 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC
+ -ENP ->LF */
+ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
+ /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB
+ -IUS */
+ 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC
+ -INP */
+ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
+ /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL
+ -SW */
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
+ /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
+ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
+ /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
+ 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
+ /* 0x40 SP RSP ä ---- */
+ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
+ /* 0x48 . < ( + | */
+ 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
+ /* 0x50 & ---- */
+ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
+ /* 0x58 ß ! $ * ) ; */
+ 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA,
+ /* 0x60 - / ---- Ä ---- ---- ---- */
+ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
+ /* 0x68 ---- , % _ > ? */
+ 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
+ /* 0x70 ---- ---- ---- ---- ---- ---- ---- */
+ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ /* 0x78 * ` : # @ ' = " */
+ 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
+ /* 0x80 * a b c d e f g */
+ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ /* 0x88 h i ---- ---- ---- */
+ 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
+ /* 0x90 ° j k l m n o p */
+ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
+ /* 0x98 q r ---- ---- */
+ 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
+ /* 0xA0 ~ s t u v w x */
+ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ /* 0xA8 y z ---- ---- ---- ---- */
+ 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
+ /* 0xB0 ^ ---- § ---- */
+ 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
+ /* 0xB8 ---- [ ] ---- ---- ---- ---- */
+ 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07,
+ /* 0xC0 { A B C D E F G */
+ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ /* 0xC8 H I ---- ö ---- */
+ 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
+ /* 0xD0 } J K L M N O P */
+ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
+ /* 0xD8 Q R ---- ü */
+ 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
+ /* 0xE0 \ S T U V W X */
+ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ /* 0xE8 Y Z ---- Ö ---- ---- ---- */
+ 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
+ /* 0xF0 0 1 2 3 4 5 6 7 */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */
+ 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
+};
+
+
+/*
+ * ASCII (IBM PC 437) -> EBCDIC 500
+ */
+__u8 _ascebc_500[256] =
+{
+ /*00 NUL SOH STX ETX EOT ENQ ACK BEL */
+ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F,
+ /*08 BS HT LF VT FF CR SO SI */
+ /* ->NL */
+ 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ /*10 DLE DC1 DC2 DC3 DC4 NAK SYN ETB */
+ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26,
+ /*18 CAN EM SUB ESC FS GS RS US */
+ /* ->IGS ->IRS ->IUS */
+ 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F,
+ /*20 SP ! " # $ % & ' */
+ 0x40, 0x4F, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D,
+ /*28 ( ) * + , - . / */
+ 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
+ /*30 0 1 2 3 4 5 6 7 */
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ /*38 8 9 : ; < = > ? */
+ 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
+ /*40 @ A B C D E F G */
+ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ /*48 H I J K L M N O */
+ 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
+ /*50 P Q R S T U V W */
+ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
+ /*58 X Y Z [ \ ] ^ _ */
+ 0xE7, 0xE8, 0xE9, 0x4A, 0xE0, 0x5A, 0x5F, 0x6D,
+ /*60 ` a b c d e f g */
+ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ /*68 h i j k l m n o */
+ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
+ /*70 p q r s t u v w */
+ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
+ /*78 x y z { | } ~ DL */
+ 0xA7, 0xA8, 0xA9, 0xC0, 0xBB, 0xD0, 0xA1, 0x07,
+ /*80*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*88*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*90*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*98*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*A0*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*A8*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*B0*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*B8*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*C0*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*C8*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*D0*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*D8*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*E0 sz */
+ 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*E8*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*F0*/
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ /*F8*/
+ 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF
+};
+
+/*
+ * EBCDIC 500 -> ASCII (IBM PC 437)
+ */
+__u8 _ebcasc_500[256] =
+{
+ /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */
+ 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F,
+ /* 0x08 -GE -SPS -RPT VT FF CR SO SI */
+ 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC
+ -ENP ->LF */
+ 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07,
+ /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB
+ -IUS */
+ 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC
+ -INP */
+ 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B,
+ /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL
+ -SW */
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07,
+ /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */
+ 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04,
+ /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */
+ 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A,
+ /* 0x40 SP RSP ä ---- */
+ 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86,
+ /* 0x48 [ . < ( + ! */
+ 0x87, 0xA4, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x21,
+ /* 0x50 & ---- */
+ 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07,
+ /* 0x58 ß ] $ * ) ; ^ */
+ 0x8D, 0xE1, 0x5D, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
+ /* 0x60 - / ---- Ä ---- ---- ---- */
+ 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F,
+ /* 0x68 ---- , % _ > ? */
+ 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
+ /* 0x70 ---- ---- ---- ---- ---- ---- ---- */
+ 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ /* 0x78 * ` : # @ ' = " */
+ 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
+ /* 0x80 * a b c d e f g */
+ 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ /* 0x88 h i ---- ---- ---- */
+ 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1,
+ /* 0x90 ° j k l m n o p */
+ 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
+ /* 0x98 q r ---- ---- */
+ 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07,
+ /* 0xA0 ~ s t u v w x */
+ 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ /* 0xA8 y z ---- ---- ---- ---- */
+ 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07,
+ /* 0xB0 ---- § ---- */
+ 0x9B, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC,
+ /* 0xB8 ---- | ---- ---- ---- ---- */
+ 0xAB, 0x07, 0xAA, 0x7C, 0x07, 0x07, 0x07, 0x07,
+ /* 0xC0 { A B C D E F G */
+ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ /* 0xC8 H I ---- ö ---- */
+ 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07,
+ /* 0xD0 } J K L M N O P */
+ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
+ /* 0xD8 Q R ---- ü */
+ 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98,
+ /* 0xE0 \ S T U V W X */
+ 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ /* 0xE8 Y Z ---- Ö ---- ---- ---- */
+ 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07,
+ /* 0xF0 0 1 2 3 4 5 6 7 */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */
+ 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07
+};
+
+
+/*
+ * EBCDIC 037/500 conversion table:
+ * from upper to lower case
+ */
+__u8 _ebc_tolower[256] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+ 0x60, 0x61, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ 0x48, 0x49, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ 0x58, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9C, 0x9F,
+ 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+ 0xA8, 0xA9, 0xAA, 0xAB, 0x8C, 0x8D, 0x8E, 0xAF,
+ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
+ 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+ 0xC0, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+ 0xD0, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+ 0xE0, 0xE1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+ 0xA8, 0xA9, 0xEA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA, 0xDB, 0xDC, 0xDD, 0xDE, 0xFF
+};
+
+
+/*
+ * EBCDIC 037/500 conversion table:
+ * from lower to upper case
+ */
+__u8 _ebc_toupper[256] =
+{
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+ 0x40, 0x41, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+ 0x50, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+ 0x80, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ 0xC8, 0xC9, 0x8A, 0x8B, 0xAC, 0xAD, 0xAE, 0x8F,
+ 0x90, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
+ 0xD8, 0xD9, 0x9A, 0x9B, 0x9E, 0x9D, 0x9E, 0x9F,
+ 0xA0, 0xA1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+ 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
+ 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+ 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ 0xC8, 0xC9, 0xCA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+ 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
+ 0xD8, 0xD9, 0xDA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF,
+ 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+ 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
+};
+
+EXPORT_SYMBOL(_ascebc_500);
+EXPORT_SYMBOL(_ebcasc_500);
+EXPORT_SYMBOL(_ascebc);
+EXPORT_SYMBOL(_ebcasc);
+EXPORT_SYMBOL(_ebc_tolower);
+EXPORT_SYMBOL(_ebc_toupper);
+
diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
new file mode 100644
index 00000000..74ee563f
--- /dev/null
+++ b/arch/s390/kernel/entry.S
@@ -0,0 +1,967 @@
+/*
+ * arch/s390/kernel/entry.S
+ * S390 low-level entry points.
+ *
+ * Copyright (C) IBM Corp. 1999,2012
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ * Hartmut Penner (hp@de.ibm.com),
+ * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <asm/cache.h>
+#include <asm/errno.h>
+#include <asm/ptrace.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+#include <asm/page.h>
+
+__PT_R0 = __PT_GPRS
+__PT_R1 = __PT_GPRS + 4
+__PT_R2 = __PT_GPRS + 8
+__PT_R3 = __PT_GPRS + 12
+__PT_R4 = __PT_GPRS + 16
+__PT_R5 = __PT_GPRS + 20
+__PT_R6 = __PT_GPRS + 24
+__PT_R7 = __PT_GPRS + 28
+__PT_R8 = __PT_GPRS + 32
+__PT_R9 = __PT_GPRS + 36
+__PT_R10 = __PT_GPRS + 40
+__PT_R11 = __PT_GPRS + 44
+__PT_R12 = __PT_GPRS + 48
+__PT_R13 = __PT_GPRS + 524
+__PT_R14 = __PT_GPRS + 56
+__PT_R15 = __PT_GPRS + 60
+
+_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
+ _TIF_MCCK_PENDING | _TIF_PER_TRAP )
+_TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
+ _TIF_MCCK_PENDING)
+_TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \
+ _TIF_SYSCALL_TRACEPOINT)
+
+STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER
+STACK_SIZE = 1 << STACK_SHIFT
+
+#define BASED(name) name-system_call(%r13)
+
+ .macro TRACE_IRQS_ON
+#ifdef CONFIG_TRACE_IRQFLAGS
+ basr %r2,%r0
+ l %r1,BASED(.Lhardirqs_on)
+ basr %r14,%r1 # call trace_hardirqs_on_caller
+#endif
+ .endm
+
+ .macro TRACE_IRQS_OFF
+#ifdef CONFIG_TRACE_IRQFLAGS
+ basr %r2,%r0
+ l %r1,BASED(.Lhardirqs_off)
+ basr %r14,%r1 # call trace_hardirqs_off_caller
+#endif
+ .endm
+
+ .macro LOCKDEP_SYS_EXIT
+#ifdef CONFIG_LOCKDEP
+ tm __PT_PSW+1(%r11),0x01 # returning to user ?
+ jz .+10
+ l %r1,BASED(.Llockdep_sys_exit)
+ basr %r14,%r1 # call lockdep_sys_exit
+#endif
+ .endm
+
+ .macro CHECK_STACK stacksize,savearea
+#ifdef CONFIG_CHECK_STACK
+ tml %r15,\stacksize - CONFIG_STACK_GUARD
+ la %r14,\savearea
+ jz stack_overflow
+#endif
+ .endm
+
+ .macro SWITCH_ASYNC savearea,stack,shift
+ tmh %r8,0x0001 # interrupting from user ?
+ jnz 1f
+ lr %r14,%r9
+ sl %r14,BASED(.Lcritical_start)
+ cl %r14,BASED(.Lcritical_length)
+ jhe 0f
+ la %r11,\savearea # inside critical section, do cleanup
+ bras %r14,cleanup_critical
+ tmh %r8,0x0001 # retest problem state after cleanup
+ jnz 1f
+0: l %r14,\stack # are we already on the target stack?
+ slr %r14,%r15
+ sra %r14,\shift
+ jnz 1f
+ CHECK_STACK 1<<\shift,\savearea
+ j 2f
+1: l %r15,\stack # load target stack
+2: ahi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ la %r11,STACK_FRAME_OVERHEAD(%r15)
+ .endm
+
+ .macro ADD64 high,low,timer
+ al \high,\timer
+ al \low,4+\timer
+ brc 12,.+8
+ ahi \high,1
+ .endm
+
+ .macro SUB64 high,low,timer
+ sl \high,\timer
+ sl \low,4+\timer
+ brc 3,.+8
+ ahi \high,-1
+ .endm
+
+ .macro UPDATE_VTIME high,low,enter_timer
+ lm \high,\low,__LC_EXIT_TIMER
+ SUB64 \high,\low,\enter_timer
+ ADD64 \high,\low,__LC_USER_TIMER
+ stm \high,\low,__LC_USER_TIMER
+ lm \high,\low,__LC_LAST_UPDATE_TIMER
+ SUB64 \high,\low,__LC_EXIT_TIMER
+ ADD64 \high,\low,__LC_SYSTEM_TIMER
+ stm \high,\low,__LC_SYSTEM_TIMER
+ mvc __LC_LAST_UPDATE_TIMER(8),\enter_timer
+ .endm
+
+ .macro REENABLE_IRQS
+ st %r8,__LC_RETURN_PSW
+ ni __LC_RETURN_PSW,0xbf
+ ssm __LC_RETURN_PSW
+ .endm
+
+ .section .kprobes.text, "ax"
+
+/*
+ * Scheduler resume function, called by switch_to
+ * gpr2 = (task_struct *) prev
+ * gpr3 = (task_struct *) next
+ * Returns:
+ * gpr2 = prev
+ */
+ENTRY(__switch_to)
+ l %r4,__THREAD_info(%r2) # get thread_info of prev
+ l %r5,__THREAD_info(%r3) # get thread_info of next
+ tm __TI_flags+3(%r4),_TIF_MCCK_PENDING # machine check pending?
+ jz 0f
+ ni __TI_flags+3(%r4),255-_TIF_MCCK_PENDING # clear flag in prev
+ oi __TI_flags+3(%r5),_TIF_MCCK_PENDING # set it in next
+0: stm %r6,%r15,__SF_GPRS(%r15) # store gprs of prev task
+ st %r15,__THREAD_ksp(%r2) # store kernel stack of prev
+ l %r15,__THREAD_ksp(%r3) # load kernel stack of next
+ lctl %c4,%c4,__TASK_pid(%r3) # load pid to control reg. 4
+ lm %r6,%r15,__SF_GPRS(%r15) # load gprs of next task
+ st %r3,__LC_CURRENT # store task struct of next
+ mvc __LC_CURRENT_PID(4,%r0),__TASK_pid(%r3) # store pid of next
+ st %r5,__LC_THREAD_INFO # store thread info of next
+ ahi %r5,STACK_SIZE # end of kernel stack of next
+ st %r5,__LC_KERNEL_STACK # store end of kernel stack
+ br %r14
+
+__critical_start:
+/*
+ * SVC interrupt handler routine. System calls are synchronous events and
+ * are executed with interrupts enabled.
+ */
+
+ENTRY(system_call)
+ stpt __LC_SYNC_ENTER_TIMER
+sysc_stm:
+ stm %r8,%r15,__LC_SAVE_AREA_SYNC
+ l %r12,__LC_THREAD_INFO
+ l %r13,__LC_SVC_NEW_PSW+4
+sysc_per:
+ l %r15,__LC_KERNEL_STACK
+ ahi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ la %r11,STACK_FRAME_OVERHEAD(%r15) # pointer to pt_regs
+sysc_vtime:
+ UPDATE_VTIME %r8,%r9,__LC_SYNC_ENTER_TIMER
+ stm %r0,%r7,__PT_R0(%r11)
+ mvc __PT_R8(32,%r11),__LC_SAVE_AREA_SYNC
+ mvc __PT_PSW(8,%r11),__LC_SVC_OLD_PSW
+ mvc __PT_INT_CODE(4,%r11),__LC_SVC_ILC
+sysc_do_svc:
+ oi __TI_flags+3(%r12),_TIF_SYSCALL
+ lh %r8,__PT_INT_CODE+2(%r11)
+ sla %r8,2 # shift and test for svc0
+ jnz sysc_nr_ok
+ # svc 0: system call number in %r1
+ cl %r1,BASED(.Lnr_syscalls)
+ jnl sysc_nr_ok
+ sth %r1,__PT_INT_CODE+2(%r11)
+ lr %r8,%r1
+ sla %r8,2
+sysc_nr_ok:
+ l %r10,BASED(.Lsys_call_table) # 31 bit system call table
+ xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
+ st %r2,__PT_ORIG_GPR2(%r11)
+ st %r7,STACK_FRAME_OVERHEAD(%r15)
+ l %r9,0(%r8,%r10) # get system call addr.
+ tm __TI_flags+2(%r12),_TIF_TRACE >> 8
+ jnz sysc_tracesys
+ basr %r14,%r9 # call sys_xxxx
+ st %r2,__PT_R2(%r11) # store return value
+
+sysc_return:
+ LOCKDEP_SYS_EXIT
+sysc_tif:
+ tm __PT_PSW+1(%r11),0x01 # returning to user ?
+ jno sysc_restore
+ tm __TI_flags+3(%r12),_TIF_WORK_SVC
+ jnz sysc_work # check for work
+ ni __TI_flags+3(%r12),255-_TIF_SYSCALL
+sysc_restore:
+ mvc __LC_RETURN_PSW(8),__PT_PSW(%r11)
+ stpt __LC_EXIT_TIMER
+ lm %r0,%r15,__PT_R0(%r11)
+ lpsw __LC_RETURN_PSW
+sysc_done:
+
+#
+# One of the work bits is on. Find out which one.
+#
+sysc_work:
+ tm __TI_flags+3(%r12),_TIF_MCCK_PENDING
+ jo sysc_mcck_pending
+ tm __TI_flags+3(%r12),_TIF_NEED_RESCHED
+ jo sysc_reschedule
+ tm __TI_flags+3(%r12),_TIF_SIGPENDING
+ jo sysc_sigpending
+ tm __TI_flags+3(%r12),_TIF_NOTIFY_RESUME
+ jo sysc_notify_resume
+ tm __TI_flags+3(%r12),_TIF_PER_TRAP
+ jo sysc_singlestep
+ j sysc_return # beware of critical section cleanup
+
+#
+# _TIF_NEED_RESCHED is set, call schedule
+#
+sysc_reschedule:
+ l %r1,BASED(.Lschedule)
+ la %r14,BASED(sysc_return)
+ br %r1 # call schedule
+
+#
+# _TIF_MCCK_PENDING is set, call handler
+#
+sysc_mcck_pending:
+ l %r1,BASED(.Lhandle_mcck)
+ la %r14,BASED(sysc_return)
+ br %r1 # TIF bit will be cleared by handler
+
+#
+# _TIF_SIGPENDING is set, call do_signal
+#
+sysc_sigpending:
+ ni __TI_flags+3(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
+ lr %r2,%r11 # pass pointer to pt_regs
+ l %r1,BASED(.Ldo_signal)
+ basr %r14,%r1 # call do_signal
+ tm __TI_flags+3(%r12),_TIF_SYSCALL
+ jno sysc_return
+ lm %r2,%r7,__PT_R2(%r11) # load svc arguments
+ xr %r8,%r8 # svc 0 returns -ENOSYS
+ clc __PT_INT_CODE+2(2,%r11),BASED(.Lnr_syscalls+2)
+ jnl sysc_nr_ok # invalid svc number -> do svc 0
+ lh %r8,__PT_INT_CODE+2(%r11) # load new svc number
+ sla %r8,2
+ j sysc_nr_ok # restart svc
+
+#
+# _TIF_NOTIFY_RESUME is set, call do_notify_resume
+#
+sysc_notify_resume:
+ lr %r2,%r11 # pass pointer to pt_regs
+ l %r1,BASED(.Ldo_notify_resume)
+ la %r14,BASED(sysc_return)
+ br %r1 # call do_notify_resume
+
+#
+# _TIF_PER_TRAP is set, call do_per_trap
+#
+sysc_singlestep:
+ ni __TI_flags+3(%r12),255-(_TIF_SYSCALL | _TIF_PER_TRAP)
+ lr %r2,%r11 # pass pointer to pt_regs
+ l %r1,BASED(.Ldo_per_trap)
+ la %r14,BASED(sysc_return)
+ br %r1 # call do_per_trap
+
+#
+# call tracehook_report_syscall_entry/tracehook_report_syscall_exit before
+# and after the system call
+#
+sysc_tracesys:
+ l %r1,BASED(.Ltrace_enter)
+ lr %r2,%r11 # pass pointer to pt_regs
+ la %r3,0
+ xr %r0,%r0
+ icm %r0,3,__PT_INT_CODE+2(%r11)
+ st %r0,__PT_R2(%r11)
+ basr %r14,%r1 # call do_syscall_trace_enter
+ cl %r2,BASED(.Lnr_syscalls)
+ jnl sysc_tracenogo
+ lr %r8,%r2
+ sll %r8,2
+ l %r9,0(%r8,%r10)
+sysc_tracego:
+ lm %r3,%r7,__PT_R3(%r11)
+ st %r7,STACK_FRAME_OVERHEAD(%r15)
+ l %r2,__PT_ORIG_GPR2(%r11)
+ basr %r14,%r9 # call sys_xxx
+ st %r2,__PT_R2(%r11) # store return value
+sysc_tracenogo:
+ tm __TI_flags+2(%r12),_TIF_TRACE >> 8
+ jz sysc_return
+ l %r1,BASED(.Ltrace_exit)
+ lr %r2,%r11 # pass pointer to pt_regs
+ la %r14,BASED(sysc_return)
+ br %r1 # call do_syscall_trace_exit
+
+#
+# a new process exits the kernel with ret_from_fork
+#
+ENTRY(ret_from_fork)
+ la %r11,STACK_FRAME_OVERHEAD(%r15)
+ l %r12,__LC_THREAD_INFO
+ l %r13,__LC_SVC_NEW_PSW+4
+ tm __PT_PSW+1(%r11),0x01 # forking a kernel thread ?
+ jo 0f
+ st %r15,__PT_R15(%r11) # store stack pointer for new kthread
+0: l %r1,BASED(.Lschedule_tail)
+ basr %r14,%r1 # call schedule_tail
+ TRACE_IRQS_ON
+ ssm __LC_SVC_NEW_PSW # reenable interrupts
+ j sysc_tracenogo
+
+#
+# kernel_execve function needs to deal with pt_regs that is not
+# at the usual place
+#
+ENTRY(kernel_execve)
+ stm %r12,%r15,48(%r15)
+ lr %r14,%r15
+ l %r13,__LC_SVC_NEW_PSW+4
+ ahi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ st %r14,__SF_BACKCHAIN(%r15)
+ la %r12,STACK_FRAME_OVERHEAD(%r15)
+ xc 0(__PT_SIZE,%r12),0(%r12)
+ l %r1,BASED(.Ldo_execve)
+ lr %r5,%r12
+ basr %r14,%r1 # call do_execve
+ ltr %r2,%r2
+ je 0f
+ ahi %r15,(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ lm %r12,%r15,48(%r15)
+ br %r14
+ # execve succeeded.
+0: ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
+ l %r15,__LC_KERNEL_STACK # load ksp
+ ahi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ la %r11,STACK_FRAME_OVERHEAD(%r15)
+ mvc 0(__PT_SIZE,%r11),0(%r12) # copy pt_regs
+ l %r12,__LC_THREAD_INFO
+ xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
+ ssm __LC_SVC_NEW_PSW # reenable interrupts
+ l %r1,BASED(.Lexecve_tail)
+ basr %r14,%r1 # call execve_tail
+ j sysc_return
+
+/*
+ * Program check handler routine
+ */
+
+ENTRY(pgm_check_handler)
+ stpt __LC_SYNC_ENTER_TIMER
+ stm %r8,%r15,__LC_SAVE_AREA_SYNC
+ l %r12,__LC_THREAD_INFO
+ l %r13,__LC_SVC_NEW_PSW+4
+ lm %r8,%r9,__LC_PGM_OLD_PSW
+ tmh %r8,0x0001 # test problem state bit
+ jnz 1f # -> fault in user space
+ tmh %r8,0x4000 # PER bit set in old PSW ?
+ jnz 0f # -> enabled, can't be a double fault
+ tm __LC_PGM_ILC+3,0x80 # check for per exception
+ jnz pgm_svcper # -> single stepped svc
+0: CHECK_STACK STACK_SIZE,__LC_SAVE_AREA_SYNC
+ j 2f
+1: UPDATE_VTIME %r14,%r15,__LC_SYNC_ENTER_TIMER
+ l %r15,__LC_KERNEL_STACK
+2: ahi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ la %r11,STACK_FRAME_OVERHEAD(%r15)
+ stm %r0,%r7,__PT_R0(%r11)
+ mvc __PT_R8(32,%r11),__LC_SAVE_AREA_SYNC
+ stm %r8,%r9,__PT_PSW(%r11)
+ mvc __PT_INT_CODE(4,%r11),__LC_PGM_ILC
+ mvc __PT_INT_PARM_LONG(4,%r11),__LC_TRANS_EXC_CODE
+ tm __LC_PGM_ILC+3,0x80 # check for per exception
+ jz 0f
+ l %r1,__TI_task(%r12)
+ tmh %r8,0x0001 # kernel per event ?
+ jz pgm_kprobe
+ oi __TI_flags+3(%r12),_TIF_PER_TRAP
+ mvc __THREAD_per_address(4,%r1),__LC_PER_ADDRESS
+ mvc __THREAD_per_cause(2,%r1),__LC_PER_CAUSE
+ mvc __THREAD_per_paid(1,%r1),__LC_PER_PAID
+0: REENABLE_IRQS
+ xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
+ l %r1,BASED(.Ljump_table)
+ la %r10,0x7f
+ n %r10,__PT_INT_CODE(%r11)
+ je sysc_return
+ sll %r10,2
+ l %r1,0(%r10,%r1) # load address of handler routine
+ lr %r2,%r11 # pass pointer to pt_regs
+ basr %r14,%r1 # branch to interrupt-handler
+ j sysc_return
+
+#
+# PER event in supervisor state, must be kprobes
+#
+pgm_kprobe:
+ REENABLE_IRQS
+ xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
+ l %r1,BASED(.Ldo_per_trap)
+ lr %r2,%r11 # pass pointer to pt_regs
+ basr %r14,%r1 # call do_per_trap
+ j sysc_return
+
+#
+# single stepped system call
+#
+pgm_svcper:
+ oi __TI_flags+3(%r12),_TIF_PER_TRAP
+ mvc __LC_RETURN_PSW(4),__LC_SVC_NEW_PSW
+ mvc __LC_RETURN_PSW+4(4),BASED(.Lsysc_per)
+ lpsw __LC_RETURN_PSW # branch to sysc_per and enable irqs
+
+/*
+ * IO interrupt handler routine
+ */
+
+ENTRY(io_int_handler)
+ stck __LC_INT_CLOCK
+ stpt __LC_ASYNC_ENTER_TIMER
+ stm %r8,%r15,__LC_SAVE_AREA_ASYNC
+ l %r12,__LC_THREAD_INFO
+ l %r13,__LC_SVC_NEW_PSW+4
+ lm %r8,%r9,__LC_IO_OLD_PSW
+ tmh %r8,0x0001 # interrupting from user ?
+ jz io_skip
+ UPDATE_VTIME %r14,%r15,__LC_ASYNC_ENTER_TIMER
+io_skip:
+ SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT
+ stm %r0,%r7,__PT_R0(%r11)
+ mvc __PT_R8(32,%r11),__LC_SAVE_AREA_ASYNC
+ stm %r8,%r9,__PT_PSW(%r11)
+ TRACE_IRQS_OFF
+ xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
+ l %r1,BASED(.Ldo_IRQ)
+ lr %r2,%r11 # pass pointer to pt_regs
+ basr %r14,%r1 # call do_IRQ
+io_return:
+ LOCKDEP_SYS_EXIT
+ TRACE_IRQS_ON
+io_tif:
+ tm __TI_flags+3(%r12),_TIF_WORK_INT
+ jnz io_work # there is work to do (signals etc.)
+io_restore:
+ mvc __LC_RETURN_PSW(8),__PT_PSW(%r11)
+ stpt __LC_EXIT_TIMER
+ lm %r0,%r15,__PT_R0(%r11)
+ lpsw __LC_RETURN_PSW
+io_done:
+
+#
+# There is work todo, find out in which context we have been interrupted:
+# 1) if we return to user space we can do all _TIF_WORK_INT work
+# 2) if we return to kernel code and preemptive scheduling is enabled check
+# the preemption counter and if it is zero call preempt_schedule_irq
+# Before any work can be done, a switch to the kernel stack is required.
+#
+io_work:
+ tm __PT_PSW+1(%r11),0x01 # returning to user ?
+ jo io_work_user # yes -> do resched & signal
+#ifdef CONFIG_PREEMPT
+ # check for preemptive scheduling
+ icm %r0,15,__TI_precount(%r12)
+ jnz io_restore # preemption disabled
+ tm __TI_flags+3(%r12),_TIF_NEED_RESCHED
+ jno io_restore
+ # switch to kernel stack
+ l %r1,__PT_R15(%r11)
+ ahi %r1,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ mvc STACK_FRAME_OVERHEAD(__PT_SIZE,%r1),0(%r11)
+ xc __SF_BACKCHAIN(4,%r1),__SF_BACKCHAIN(%r1)
+ la %r11,STACK_FRAME_OVERHEAD(%r1)
+ lr %r15,%r1
+ # TRACE_IRQS_ON already done at io_return, call
+ # TRACE_IRQS_OFF to keep things symmetrical
+ TRACE_IRQS_OFF
+ l %r1,BASED(.Lpreempt_irq)
+ basr %r14,%r1 # call preempt_schedule_irq
+ j io_return
+#else
+ j io_restore
+#endif
+
+#
+# Need to do work before returning to userspace, switch to kernel stack
+#
+io_work_user:
+ l %r1,__LC_KERNEL_STACK
+ ahi %r1,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ mvc STACK_FRAME_OVERHEAD(__PT_SIZE,%r1),0(%r11)
+ xc __SF_BACKCHAIN(4,%r1),__SF_BACKCHAIN(%r1)
+ la %r11,STACK_FRAME_OVERHEAD(%r1)
+ lr %r15,%r1
+
+#
+# One of the work bits is on. Find out which one.
+# Checked are: _TIF_SIGPENDING, _TIF_NOTIFY_RESUME, _TIF_NEED_RESCHED
+# and _TIF_MCCK_PENDING
+#
+io_work_tif:
+ tm __TI_flags+3(%r12),_TIF_MCCK_PENDING
+ jo io_mcck_pending
+ tm __TI_flags+3(%r12),_TIF_NEED_RESCHED
+ jo io_reschedule
+ tm __TI_flags+3(%r12),_TIF_SIGPENDING
+ jo io_sigpending
+ tm __TI_flags+3(%r12),_TIF_NOTIFY_RESUME
+ jo io_notify_resume
+ j io_return # beware of critical section cleanup
+
+#
+# _TIF_MCCK_PENDING is set, call handler
+#
+io_mcck_pending:
+ # TRACE_IRQS_ON already done at io_return
+ l %r1,BASED(.Lhandle_mcck)
+ basr %r14,%r1 # TIF bit will be cleared by handler
+ TRACE_IRQS_OFF
+ j io_return
+
+#
+# _TIF_NEED_RESCHED is set, call schedule
+#
+io_reschedule:
+ # TRACE_IRQS_ON already done at io_return
+ l %r1,BASED(.Lschedule)
+ ssm __LC_SVC_NEW_PSW # reenable interrupts
+ basr %r14,%r1 # call scheduler
+ ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
+ TRACE_IRQS_OFF
+ j io_return
+
+#
+# _TIF_SIGPENDING is set, call do_signal
+#
+io_sigpending:
+ # TRACE_IRQS_ON already done at io_return
+ l %r1,BASED(.Ldo_signal)
+ ssm __LC_SVC_NEW_PSW # reenable interrupts
+ lr %r2,%r11 # pass pointer to pt_regs
+ basr %r14,%r1 # call do_signal
+ ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
+ TRACE_IRQS_OFF
+ j io_return
+
+#
+# _TIF_SIGPENDING is set, call do_signal
+#
+io_notify_resume:
+ # TRACE_IRQS_ON already done at io_return
+ l %r1,BASED(.Ldo_notify_resume)
+ ssm __LC_SVC_NEW_PSW # reenable interrupts
+ lr %r2,%r11 # pass pointer to pt_regs
+ basr %r14,%r1 # call do_notify_resume
+ ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
+ TRACE_IRQS_OFF
+ j io_return
+
+/*
+ * External interrupt handler routine
+ */
+
+ENTRY(ext_int_handler)
+ stck __LC_INT_CLOCK
+ stpt __LC_ASYNC_ENTER_TIMER
+ stm %r8,%r15,__LC_SAVE_AREA_ASYNC
+ l %r12,__LC_THREAD_INFO
+ l %r13,__LC_SVC_NEW_PSW+4
+ lm %r8,%r9,__LC_EXT_OLD_PSW
+ tmh %r8,0x0001 # interrupting from user ?
+ jz ext_skip
+ UPDATE_VTIME %r14,%r15,__LC_ASYNC_ENTER_TIMER
+ext_skip:
+ SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT
+ stm %r0,%r7,__PT_R0(%r11)
+ mvc __PT_R8(32,%r11),__LC_SAVE_AREA_ASYNC
+ stm %r8,%r9,__PT_PSW(%r11)
+ TRACE_IRQS_OFF
+ lr %r2,%r11 # pass pointer to pt_regs
+ l %r3,__LC_EXT_CPU_ADDR # get cpu address + interruption code
+ l %r4,__LC_EXT_PARAMS # get external parameters
+ l %r1,BASED(.Ldo_extint)
+ basr %r14,%r1 # call do_extint
+ j io_return
+
+/*
+ * Load idle PSW. The second "half" of this function is in cleanup_idle.
+ */
+ENTRY(psw_idle)
+ st %r4,__SF_EMPTY(%r15)
+ basr %r1,0
+ la %r1,psw_idle_lpsw+4-.(%r1)
+ st %r1,__SF_EMPTY+4(%r15)
+ oi __SF_EMPTY+4(%r15),0x80
+ la %r1,.Lvtimer_max-psw_idle_lpsw-4(%r1)
+ stck __IDLE_ENTER(%r2)
+ ltr %r5,%r5
+ stpt __VQ_IDLE_ENTER(%r3)
+ jz psw_idle_lpsw
+ spt 0(%r1)
+psw_idle_lpsw:
+ lpsw __SF_EMPTY(%r15)
+ br %r14
+psw_idle_end:
+
+__critical_end:
+
+/*
+ * Machine check handler routines
+ */
+
+ENTRY(mcck_int_handler)
+ stck __LC_MCCK_CLOCK
+ spt __LC_CPU_TIMER_SAVE_AREA # revalidate cpu timer
+ lm %r0,%r15,__LC_GPREGS_SAVE_AREA # revalidate gprs
+ l %r12,__LC_THREAD_INFO
+ l %r13,__LC_SVC_NEW_PSW+4
+ lm %r8,%r9,__LC_MCK_OLD_PSW
+ tm __LC_MCCK_CODE,0x80 # system damage?
+ jo mcck_panic # yes -> rest of mcck code invalid
+ la %r14,__LC_CPU_TIMER_SAVE_AREA
+ mvc __LC_MCCK_ENTER_TIMER(8),0(%r14)
+ tm __LC_MCCK_CODE+5,0x02 # stored cpu timer value valid?
+ jo 3f
+ la %r14,__LC_SYNC_ENTER_TIMER
+ clc 0(8,%r14),__LC_ASYNC_ENTER_TIMER
+ jl 0f
+ la %r14,__LC_ASYNC_ENTER_TIMER
+0: clc 0(8,%r14),__LC_EXIT_TIMER
+ jl 1f
+ la %r14,__LC_EXIT_TIMER
+1: clc 0(8,%r14),__LC_LAST_UPDATE_TIMER
+ jl 2f
+ la %r14,__LC_LAST_UPDATE_TIMER
+2: spt 0(%r14)
+ mvc __LC_MCCK_ENTER_TIMER(8),0(%r14)
+3: tm __LC_MCCK_CODE+2,0x09 # mwp + ia of old psw valid?
+ jno mcck_panic # no -> skip cleanup critical
+ tm %r8,0x0001 # interrupting from user ?
+ jz mcck_skip
+ UPDATE_VTIME %r14,%r15,__LC_MCCK_ENTER_TIMER
+mcck_skip:
+ SWITCH_ASYNC __LC_GPREGS_SAVE_AREA+32,__LC_PANIC_STACK,PAGE_SHIFT
+ mvc __PT_R0(64,%r11),__LC_GPREGS_SAVE_AREA
+ stm %r8,%r9,__PT_PSW(%r11)
+ xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
+ l %r1,BASED(.Ldo_machine_check)
+ lr %r2,%r11 # pass pointer to pt_regs
+ basr %r14,%r1 # call s390_do_machine_check
+ tm __PT_PSW+1(%r11),0x01 # returning to user ?
+ jno mcck_return
+ l %r1,__LC_KERNEL_STACK # switch to kernel stack
+ ahi %r1,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ mvc STACK_FRAME_OVERHEAD(__PT_SIZE,%r1),0(%r11)
+ xc __SF_BACKCHAIN(4,%r1),__SF_BACKCHAIN(%r1)
+ la %r11,STACK_FRAME_OVERHEAD(%r15)
+ lr %r15,%r1
+ ssm __LC_PGM_NEW_PSW # turn dat on, keep irqs off
+ tm __TI_flags+3(%r12),_TIF_MCCK_PENDING
+ jno mcck_return
+ TRACE_IRQS_OFF
+ l %r1,BASED(.Lhandle_mcck)
+ basr %r14,%r1 # call s390_handle_mcck
+ TRACE_IRQS_ON
+mcck_return:
+ mvc __LC_RETURN_MCCK_PSW(8),__PT_PSW(%r11) # move return PSW
+ tm __LC_RETURN_MCCK_PSW+1,0x01 # returning to user ?
+ jno 0f
+ lm %r0,%r15,__PT_R0(%r11)
+ stpt __LC_EXIT_TIMER
+ lpsw __LC_RETURN_MCCK_PSW
+0: lm %r0,%r15,__PT_R0(%r11)
+ lpsw __LC_RETURN_MCCK_PSW
+
+mcck_panic:
+ l %r14,__LC_PANIC_STACK
+ slr %r14,%r15
+ sra %r14,PAGE_SHIFT
+ jz 0f
+ l %r15,__LC_PANIC_STACK
+0: ahi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ j mcck_skip
+
+#
+# PSW restart interrupt handler
+#
+ENTRY(restart_int_handler)
+ st %r15,__LC_SAVE_AREA_RESTART
+ l %r15,__LC_RESTART_STACK
+ ahi %r15,-__PT_SIZE # create pt_regs on stack
+ xc 0(__PT_SIZE,%r15),0(%r15)
+ stm %r0,%r14,__PT_R0(%r15)
+ mvc __PT_R15(4,%r15),__LC_SAVE_AREA_RESTART
+ mvc __PT_PSW(8,%r15),__LC_RST_OLD_PSW # store restart old psw
+ ahi %r15,-STACK_FRAME_OVERHEAD # create stack frame on stack
+ xc 0(STACK_FRAME_OVERHEAD,%r15),0(%r15)
+ lm %r1,%r3,__LC_RESTART_FN # load fn, parm & source cpu
+ ltr %r3,%r3 # test source cpu address
+ jm 1f # negative -> skip source stop
+0: sigp %r4,%r3,1 # sigp sense to source cpu
+ brc 10,0b # wait for status stored
+1: basr %r14,%r1 # call function
+ stap __SF_EMPTY(%r15) # store cpu address
+ lh %r3,__SF_EMPTY(%r15)
+2: sigp %r4,%r3,5 # sigp stop to current cpu
+ brc 2,2b
+3: j 3b
+
+ .section .kprobes.text, "ax"
+
+#ifdef CONFIG_CHECK_STACK
+/*
+ * The synchronous or the asynchronous stack overflowed. We are dead.
+ * No need to properly save the registers, we are going to panic anyway.
+ * Setup a pt_regs so that show_trace can provide a good call trace.
+ */
+stack_overflow:
+ l %r15,__LC_PANIC_STACK # change to panic stack
+ ahi %r15,-__PT_SIZE # create pt_regs
+ stm %r0,%r7,__PT_R0(%r15)
+ stm %r8,%r9,__PT_PSW(%r15)
+ mvc __PT_R8(32,%r11),0(%r14)
+ lr %r15,%r11
+ ahi %r15,-STACK_FRAME_OVERHEAD
+ l %r1,BASED(1f)
+ xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15)
+ lr %r2,%r11 # pass pointer to pt_regs
+ br %r1 # branch to kernel_stack_overflow
+1: .long kernel_stack_overflow
+#endif
+
+cleanup_table:
+ .long system_call + 0x80000000
+ .long sysc_do_svc + 0x80000000
+ .long sysc_tif + 0x80000000
+ .long sysc_restore + 0x80000000
+ .long sysc_done + 0x80000000
+ .long io_tif + 0x80000000
+ .long io_restore + 0x80000000
+ .long io_done + 0x80000000
+ .long psw_idle + 0x80000000
+ .long psw_idle_end + 0x80000000
+
+cleanup_critical:
+ cl %r9,BASED(cleanup_table) # system_call
+ jl 0f
+ cl %r9,BASED(cleanup_table+4) # sysc_do_svc
+ jl cleanup_system_call
+ cl %r9,BASED(cleanup_table+8) # sysc_tif
+ jl 0f
+ cl %r9,BASED(cleanup_table+12) # sysc_restore
+ jl cleanup_sysc_tif
+ cl %r9,BASED(cleanup_table+16) # sysc_done
+ jl cleanup_sysc_restore
+ cl %r9,BASED(cleanup_table+20) # io_tif
+ jl 0f
+ cl %r9,BASED(cleanup_table+24) # io_restore
+ jl cleanup_io_tif
+ cl %r9,BASED(cleanup_table+28) # io_done
+ jl cleanup_io_restore
+ cl %r9,BASED(cleanup_table+32) # psw_idle
+ jl 0f
+ cl %r9,BASED(cleanup_table+36) # psw_idle_end
+ jl cleanup_idle
+0: br %r14
+
+cleanup_system_call:
+ # check if stpt has been executed
+ cl %r9,BASED(cleanup_system_call_insn)
+ jh 0f
+ mvc __LC_SYNC_ENTER_TIMER(8),__LC_ASYNC_ENTER_TIMER
+ chi %r11,__LC_SAVE_AREA_ASYNC
+ je 0f
+ mvc __LC_SYNC_ENTER_TIMER(8),__LC_MCCK_ENTER_TIMER
+0: # check if stm has been executed
+ cl %r9,BASED(cleanup_system_call_insn+4)
+ jh 0f
+ mvc __LC_SAVE_AREA_SYNC(32),0(%r11)
+0: # set up saved registers r12, and r13
+ st %r12,16(%r11) # r12 thread-info pointer
+ st %r13,20(%r11) # r13 literal-pool pointer
+ # check if the user time calculation has been done
+ cl %r9,BASED(cleanup_system_call_insn+8)
+ jh 0f
+ l %r10,__LC_EXIT_TIMER
+ l %r15,__LC_EXIT_TIMER+4
+ SUB64 %r10,%r15,__LC_SYNC_ENTER_TIMER
+ ADD64 %r10,%r15,__LC_USER_TIMER
+ st %r10,__LC_USER_TIMER
+ st %r15,__LC_USER_TIMER+4
+0: # check if the system time calculation has been done
+ cl %r9,BASED(cleanup_system_call_insn+12)
+ jh 0f
+ l %r10,__LC_LAST_UPDATE_TIMER
+ l %r15,__LC_LAST_UPDATE_TIMER+4
+ SUB64 %r10,%r15,__LC_EXIT_TIMER
+ ADD64 %r10,%r15,__LC_SYSTEM_TIMER
+ st %r10,__LC_SYSTEM_TIMER
+ st %r15,__LC_SYSTEM_TIMER+4
+0: # update accounting time stamp
+ mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
+ # set up saved register 11
+ l %r15,__LC_KERNEL_STACK
+ ahi %r15,-__PT_SIZE
+ st %r15,12(%r11) # r11 pt_regs pointer
+ # fill pt_regs
+ mvc __PT_R8(32,%r15),__LC_SAVE_AREA_SYNC
+ stm %r0,%r7,__PT_R0(%r15)
+ mvc __PT_PSW(8,%r15),__LC_SVC_OLD_PSW
+ mvc __PT_INT_CODE(4,%r15),__LC_SVC_ILC
+ # setup saved register 15
+ ahi %r15,-STACK_FRAME_OVERHEAD
+ st %r15,28(%r11) # r15 stack pointer
+ # set new psw address and exit
+ l %r9,BASED(cleanup_table+4) # sysc_do_svc + 0x80000000
+ br %r14
+cleanup_system_call_insn:
+ .long system_call + 0x80000000
+ .long sysc_stm + 0x80000000
+ .long sysc_vtime + 0x80000000 + 36
+ .long sysc_vtime + 0x80000000 + 76
+
+cleanup_sysc_tif:
+ l %r9,BASED(cleanup_table+8) # sysc_tif + 0x80000000
+ br %r14
+
+cleanup_sysc_restore:
+ cl %r9,BASED(cleanup_sysc_restore_insn)
+ jhe 0f
+ l %r9,12(%r11) # get saved pointer to pt_regs
+ mvc __LC_RETURN_PSW(8),__PT_PSW(%r9)
+ mvc 0(32,%r11),__PT_R8(%r9)
+ lm %r0,%r7,__PT_R0(%r9)
+0: lm %r8,%r9,__LC_RETURN_PSW
+ br %r14
+cleanup_sysc_restore_insn:
+ .long sysc_done - 4 + 0x80000000
+
+cleanup_io_tif:
+ l %r9,BASED(cleanup_table+20) # io_tif + 0x80000000
+ br %r14
+
+cleanup_io_restore:
+ cl %r9,BASED(cleanup_io_restore_insn)
+ jhe 0f
+ l %r9,12(%r11) # get saved r11 pointer to pt_regs
+ mvc __LC_RETURN_PSW(8),__PT_PSW(%r9)
+ mvc 0(32,%r11),__PT_R8(%r9)
+ lm %r0,%r7,__PT_R0(%r9)
+0: lm %r8,%r9,__LC_RETURN_PSW
+ br %r14
+cleanup_io_restore_insn:
+ .long io_done - 4 + 0x80000000
+
+cleanup_idle:
+ # copy interrupt clock & cpu timer
+ mvc __IDLE_EXIT(8,%r2),__LC_INT_CLOCK
+ mvc __VQ_IDLE_EXIT(8,%r3),__LC_ASYNC_ENTER_TIMER
+ chi %r11,__LC_SAVE_AREA_ASYNC
+ je 0f
+ mvc __IDLE_EXIT(8,%r2),__LC_MCCK_CLOCK
+ mvc __VQ_IDLE_EXIT(8,%r3),__LC_MCCK_ENTER_TIMER
+0: # check if stck has been executed
+ cl %r9,BASED(cleanup_idle_insn)
+ jhe 1f
+ mvc __IDLE_ENTER(8,%r2),__IDLE_EXIT(%r2)
+ mvc __VQ_IDLE_ENTER(8,%r3),__VQ_IDLE_EXIT(%r3)
+ j 2f
+1: # check if the cpu timer has been reprogrammed
+ ltr %r5,%r5
+ jz 2f
+ spt __VQ_IDLE_ENTER(%r3)
+2: # account system time going idle
+ lm %r9,%r10,__LC_STEAL_TIMER
+ ADD64 %r9,%r10,__IDLE_ENTER(%r2)
+ SUB64 %r9,%r10,__LC_LAST_UPDATE_CLOCK
+ stm %r9,%r10,__LC_STEAL_TIMER
+ mvc __LC_LAST_UPDATE_CLOCK(8),__IDLE_EXIT(%r2)
+ lm %r9,%r10,__LC_SYSTEM_TIMER
+ ADD64 %r9,%r10,__LC_LAST_UPDATE_TIMER
+ SUB64 %r9,%r10,__VQ_IDLE_ENTER(%r3)
+ stm %r9,%r10,__LC_SYSTEM_TIMER
+ mvc __LC_LAST_UPDATE_TIMER(8),__VQ_IDLE_EXIT(%r3)
+ # prepare return psw
+ n %r8,BASED(cleanup_idle_wait) # clear wait state bit
+ l %r9,24(%r11) # return from psw_idle
+ br %r14
+cleanup_idle_insn:
+ .long psw_idle_lpsw + 0x80000000
+cleanup_idle_wait:
+ .long 0xfffdffff
+
+/*
+ * Integer constants
+ */
+ .align 4
+.Lnr_syscalls:
+ .long NR_syscalls
+.Lvtimer_max:
+ .quad 0x7fffffffffffffff
+
+/*
+ * Symbol constants
+ */
+.Ldo_machine_check: .long s390_do_machine_check
+.Lhandle_mcck: .long s390_handle_mcck
+.Ldo_IRQ: .long do_IRQ
+.Ldo_extint: .long do_extint
+.Ldo_signal: .long do_signal
+.Ldo_notify_resume: .long do_notify_resume
+.Ldo_per_trap: .long do_per_trap
+.Ldo_execve: .long do_execve
+.Lexecve_tail: .long execve_tail
+.Ljump_table: .long pgm_check_table
+.Lschedule: .long schedule
+#ifdef CONFIG_PREEMPT
+.Lpreempt_irq: .long preempt_schedule_irq
+#endif
+.Ltrace_enter: .long do_syscall_trace_enter
+.Ltrace_exit: .long do_syscall_trace_exit
+.Lschedule_tail: .long schedule_tail
+.Lsys_call_table: .long sys_call_table
+.Lsysc_per: .long sysc_per + 0x80000000
+#ifdef CONFIG_TRACE_IRQFLAGS
+.Lhardirqs_on: .long trace_hardirqs_on_caller
+.Lhardirqs_off: .long trace_hardirqs_off_caller
+#endif
+#ifdef CONFIG_LOCKDEP
+.Llockdep_sys_exit: .long lockdep_sys_exit
+#endif
+.Lcritical_start: .long __critical_start + 0x80000000
+.Lcritical_length: .long __critical_end - __critical_start
+
+ .section .rodata, "a"
+#define SYSCALL(esa,esame,emu) .long esa
+ .globl sys_call_table
+sys_call_table:
+#include "syscalls.S"
+#undef SYSCALL
diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h
new file mode 100644
index 00000000..6cdddac9
--- /dev/null
+++ b/arch/s390/kernel/entry.h
@@ -0,0 +1,75 @@
+#ifndef _ENTRY_H
+#define _ENTRY_H
+
+#include <linux/types.h>
+#include <linux/signal.h>
+#include <asm/ptrace.h>
+#include <asm/cputime.h>
+#include <asm/timer.h>
+
+extern void (*pgm_check_table[128])(struct pt_regs *);
+extern void *restart_stack;
+
+void system_call(void);
+void pgm_check_handler(void);
+void ext_int_handler(void);
+void io_int_handler(void);
+void mcck_int_handler(void);
+void restart_int_handler(void);
+void restart_call_handler(void);
+void psw_idle(struct s390_idle_data *, struct vtimer_queue *,
+ unsigned long, int);
+
+asmlinkage long do_syscall_trace_enter(struct pt_regs *regs);
+asmlinkage void do_syscall_trace_exit(struct pt_regs *regs);
+
+void do_protection_exception(struct pt_regs *regs);
+void do_dat_exception(struct pt_regs *regs);
+void do_asce_exception(struct pt_regs *regs);
+
+void do_per_trap(struct pt_regs *regs);
+void syscall_trace(struct pt_regs *regs, int entryexit);
+void kernel_stack_overflow(struct pt_regs * regs);
+void do_signal(struct pt_regs *regs);
+int handle_signal32(unsigned long sig, struct k_sigaction *ka,
+ siginfo_t *info, sigset_t *oldset, struct pt_regs *regs);
+void do_notify_resume(struct pt_regs *regs);
+
+struct ext_code;
+void do_extint(struct pt_regs *regs, struct ext_code, unsigned int, unsigned long);
+void do_restart(void);
+void __init startup_init(void);
+void die(struct pt_regs *regs, const char *str);
+
+void __init time_init(void);
+
+struct s390_mmap_arg_struct;
+struct fadvise64_64_args;
+struct old_sigaction;
+
+long sys_mmap2(struct s390_mmap_arg_struct __user *arg);
+long sys_s390_ipc(uint call, int first, unsigned long second,
+ unsigned long third, void __user *ptr);
+long sys_s390_personality(unsigned int personality);
+long sys_s390_fadvise64(int fd, u32 offset_high, u32 offset_low,
+ size_t len, int advice);
+long sys_s390_fadvise64_64(struct fadvise64_64_args __user *args);
+long sys_s390_fallocate(int fd, int mode, loff_t offset, u32 len_high,
+ u32 len_low);
+long sys_fork(void);
+long sys_clone(unsigned long newsp, unsigned long clone_flags,
+ int __user *parent_tidptr, int __user *child_tidptr);
+long sys_vfork(void);
+void execve_tail(void);
+long sys_execve(const char __user *name, const char __user *const __user *argv,
+ const char __user *const __user *envp);
+long sys_sigsuspend(int history0, int history1, old_sigset_t mask);
+long sys_sigaction(int sig, const struct old_sigaction __user *act,
+ struct old_sigaction __user *oact);
+long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss);
+long sys_sigreturn(void);
+long sys_rt_sigreturn(void);
+long sys32_sigreturn(void);
+long sys32_rt_sigreturn(void);
+
+#endif /* _ENTRY_H */
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S
new file mode 100644
index 00000000..4e1c292f
--- /dev/null
+++ b/arch/s390/kernel/entry64.S
@@ -0,0 +1,1030 @@
+/*
+ * arch/s390/kernel/entry64.S
+ * S390 low-level entry points.
+ *
+ * Copyright (C) IBM Corp. 1999,2012
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ * Hartmut Penner (hp@de.ibm.com),
+ * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <asm/cache.h>
+#include <asm/errno.h>
+#include <asm/ptrace.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+#include <asm/page.h>
+
+__PT_R0 = __PT_GPRS
+__PT_R1 = __PT_GPRS + 8
+__PT_R2 = __PT_GPRS + 16
+__PT_R3 = __PT_GPRS + 24
+__PT_R4 = __PT_GPRS + 32
+__PT_R5 = __PT_GPRS + 40
+__PT_R6 = __PT_GPRS + 48
+__PT_R7 = __PT_GPRS + 56
+__PT_R8 = __PT_GPRS + 64
+__PT_R9 = __PT_GPRS + 72
+__PT_R10 = __PT_GPRS + 80
+__PT_R11 = __PT_GPRS + 88
+__PT_R12 = __PT_GPRS + 96
+__PT_R13 = __PT_GPRS + 104
+__PT_R14 = __PT_GPRS + 112
+__PT_R15 = __PT_GPRS + 120
+
+STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER
+STACK_SIZE = 1 << STACK_SHIFT
+
+_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
+ _TIF_MCCK_PENDING | _TIF_PER_TRAP )
+_TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \
+ _TIF_MCCK_PENDING)
+_TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \
+ _TIF_SYSCALL_TRACEPOINT)
+_TIF_EXIT_SIE = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_MCCK_PENDING)
+
+#define BASED(name) name-system_call(%r13)
+
+ .macro TRACE_IRQS_ON
+#ifdef CONFIG_TRACE_IRQFLAGS
+ basr %r2,%r0
+ brasl %r14,trace_hardirqs_on_caller
+#endif
+ .endm
+
+ .macro TRACE_IRQS_OFF
+#ifdef CONFIG_TRACE_IRQFLAGS
+ basr %r2,%r0
+ brasl %r14,trace_hardirqs_off_caller
+#endif
+ .endm
+
+ .macro LOCKDEP_SYS_EXIT
+#ifdef CONFIG_LOCKDEP
+ tm __PT_PSW+1(%r11),0x01 # returning to user ?
+ jz .+10
+ brasl %r14,lockdep_sys_exit
+#endif
+ .endm
+
+ .macro SPP newpp
+#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
+ tm __LC_MACHINE_FLAGS+6,0x20 # MACHINE_FLAG_SPP
+ jz .+8
+ .insn s,0xb2800000,\newpp
+#endif
+ .endm
+
+ .macro HANDLE_SIE_INTERCEPT scratch
+#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
+ tm __TI_flags+6(%r12),_TIF_SIE>>8
+ jz .+42
+ tm __LC_MACHINE_FLAGS+6,0x20 # MACHINE_FLAG_SPP
+ jz .+8
+ .insn s,0xb2800000,BASED(.Lhost_id) # set host id
+ lgr \scratch,%r9
+ slg \scratch,BASED(.Lsie_loop)
+ clg \scratch,BASED(.Lsie_length)
+ jhe .+10
+ lg %r9,BASED(.Lsie_loop)
+#endif
+ .endm
+
+ .macro CHECK_STACK stacksize,savearea
+#ifdef CONFIG_CHECK_STACK
+ tml %r15,\stacksize - CONFIG_STACK_GUARD
+ lghi %r14,\savearea
+ jz stack_overflow
+#endif
+ .endm
+
+ .macro SWITCH_ASYNC savearea,stack,shift
+ tmhh %r8,0x0001 # interrupting from user ?
+ jnz 1f
+ lgr %r14,%r9
+ slg %r14,BASED(.Lcritical_start)
+ clg %r14,BASED(.Lcritical_length)
+ jhe 0f
+ lghi %r11,\savearea # inside critical section, do cleanup
+ brasl %r14,cleanup_critical
+ tmhh %r8,0x0001 # retest problem state after cleanup
+ jnz 1f
+0: lg %r14,\stack # are we already on the target stack?
+ slgr %r14,%r15
+ srag %r14,%r14,\shift
+ jnz 1f
+ CHECK_STACK 1<<\shift,\savearea
+ j 2f
+1: lg %r15,\stack # load target stack
+2: aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ la %r11,STACK_FRAME_OVERHEAD(%r15)
+ .endm
+
+ .macro UPDATE_VTIME scratch,enter_timer
+ lg \scratch,__LC_EXIT_TIMER
+ slg \scratch,\enter_timer
+ alg \scratch,__LC_USER_TIMER
+ stg \scratch,__LC_USER_TIMER
+ lg \scratch,__LC_LAST_UPDATE_TIMER
+ slg \scratch,__LC_EXIT_TIMER
+ alg \scratch,__LC_SYSTEM_TIMER
+ stg \scratch,__LC_SYSTEM_TIMER
+ mvc __LC_LAST_UPDATE_TIMER(8),\enter_timer
+ .endm
+
+ .macro LAST_BREAK scratch
+ srag \scratch,%r10,23
+ jz .+10
+ stg %r10,__TI_last_break(%r12)
+ .endm
+
+ .macro REENABLE_IRQS
+ stg %r8,__LC_RETURN_PSW
+ ni __LC_RETURN_PSW,0xbf
+ ssm __LC_RETURN_PSW
+ .endm
+
+ .section .kprobes.text, "ax"
+
+/*
+ * Scheduler resume function, called by switch_to
+ * gpr2 = (task_struct *) prev
+ * gpr3 = (task_struct *) next
+ * Returns:
+ * gpr2 = prev
+ */
+ENTRY(__switch_to)
+ lg %r4,__THREAD_info(%r2) # get thread_info of prev
+ lg %r5,__THREAD_info(%r3) # get thread_info of next
+ tm __TI_flags+7(%r4),_TIF_MCCK_PENDING # machine check pending?
+ jz 0f
+ ni __TI_flags+7(%r4),255-_TIF_MCCK_PENDING # clear flag in prev
+ oi __TI_flags+7(%r5),_TIF_MCCK_PENDING # set it in next
+0: stmg %r6,%r15,__SF_GPRS(%r15) # store gprs of prev task
+ stg %r15,__THREAD_ksp(%r2) # store kernel stack of prev
+ lg %r15,__THREAD_ksp(%r3) # load kernel stack of next
+ lctl %c4,%c4,__TASK_pid(%r3) # load pid to control reg. 4
+ lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task
+ stg %r3,__LC_CURRENT # store task struct of next
+ mvc __LC_CURRENT_PID+4(4,%r0),__TASK_pid(%r3) # store pid of next
+ stg %r5,__LC_THREAD_INFO # store thread info of next
+ aghi %r5,STACK_SIZE # end of kernel stack of next
+ stg %r5,__LC_KERNEL_STACK # store end of kernel stack
+ br %r14
+
+__critical_start:
+/*
+ * SVC interrupt handler routine. System calls are synchronous events and
+ * are executed with interrupts enabled.
+ */
+
+ENTRY(system_call)
+ stpt __LC_SYNC_ENTER_TIMER
+sysc_stmg:
+ stmg %r8,%r15,__LC_SAVE_AREA_SYNC
+ lg %r10,__LC_LAST_BREAK
+ lg %r12,__LC_THREAD_INFO
+ larl %r13,system_call
+sysc_per:
+ lg %r15,__LC_KERNEL_STACK
+ aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ la %r11,STACK_FRAME_OVERHEAD(%r15) # pointer to pt_regs
+sysc_vtime:
+ UPDATE_VTIME %r13,__LC_SYNC_ENTER_TIMER
+ LAST_BREAK %r13
+ stmg %r0,%r7,__PT_R0(%r11)
+ mvc __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC
+ mvc __PT_PSW(16,%r11),__LC_SVC_OLD_PSW
+ mvc __PT_INT_CODE(4,%r11),__LC_SVC_ILC
+sysc_do_svc:
+ oi __TI_flags+7(%r12),_TIF_SYSCALL
+ llgh %r8,__PT_INT_CODE+2(%r11)
+ slag %r8,%r8,2 # shift and test for svc 0
+ jnz sysc_nr_ok
+ # svc 0: system call number in %r1
+ llgfr %r1,%r1 # clear high word in r1
+ cghi %r1,NR_syscalls
+ jnl sysc_nr_ok
+ sth %r1,__PT_INT_CODE+2(%r11)
+ slag %r8,%r1,2
+sysc_nr_ok:
+ larl %r10,sys_call_table # 64 bit system call table
+#ifdef CONFIG_COMPAT
+ tm __TI_flags+5(%r12),(_TIF_31BIT>>16)
+ jno sysc_noemu
+ larl %r10,sys_call_table_emu # 31 bit system call table
+sysc_noemu:
+#endif
+ xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
+ stg %r2,__PT_ORIG_GPR2(%r11)
+ stg %r7,STACK_FRAME_OVERHEAD(%r15)
+ lgf %r9,0(%r8,%r10) # get system call add.
+ tm __TI_flags+6(%r12),_TIF_TRACE >> 8
+ jnz sysc_tracesys
+ basr %r14,%r9 # call sys_xxxx
+ stg %r2,__PT_R2(%r11) # store return value
+
+sysc_return:
+ LOCKDEP_SYS_EXIT
+sysc_tif:
+ tm __PT_PSW+1(%r11),0x01 # returning to user ?
+ jno sysc_restore
+ tm __TI_flags+7(%r12),_TIF_WORK_SVC
+ jnz sysc_work # check for work
+ ni __TI_flags+7(%r12),255-_TIF_SYSCALL
+sysc_restore:
+ lg %r14,__LC_VDSO_PER_CPU
+ lmg %r0,%r10,__PT_R0(%r11)
+ mvc __LC_RETURN_PSW(16),__PT_PSW(%r11)
+ stpt __LC_EXIT_TIMER
+ mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER
+ lmg %r11,%r15,__PT_R11(%r11)
+ lpswe __LC_RETURN_PSW
+sysc_done:
+
+#
+# One of the work bits is on. Find out which one.
+#
+sysc_work:
+ tm __TI_flags+7(%r12),_TIF_MCCK_PENDING
+ jo sysc_mcck_pending
+ tm __TI_flags+7(%r12),_TIF_NEED_RESCHED
+ jo sysc_reschedule
+ tm __TI_flags+7(%r12),_TIF_SIGPENDING
+ jo sysc_sigpending
+ tm __TI_flags+7(%r12),_TIF_NOTIFY_RESUME
+ jo sysc_notify_resume
+ tm __TI_flags+7(%r12),_TIF_PER_TRAP
+ jo sysc_singlestep
+ j sysc_return # beware of critical section cleanup
+
+#
+# _TIF_NEED_RESCHED is set, call schedule
+#
+sysc_reschedule:
+ larl %r14,sysc_return
+ jg schedule
+
+#
+# _TIF_MCCK_PENDING is set, call handler
+#
+sysc_mcck_pending:
+ larl %r14,sysc_return
+ jg s390_handle_mcck # TIF bit will be cleared by handler
+
+#
+# _TIF_SIGPENDING is set, call do_signal
+#
+sysc_sigpending:
+ ni __TI_flags+7(%r12),255-_TIF_PER_TRAP # clear TIF_PER_TRAP
+ lgr %r2,%r11 # pass pointer to pt_regs
+ brasl %r14,do_signal
+ tm __TI_flags+7(%r12),_TIF_SYSCALL
+ jno sysc_return
+ lmg %r2,%r7,__PT_R2(%r11) # load svc arguments
+ lghi %r8,0 # svc 0 returns -ENOSYS
+ lh %r1,__PT_INT_CODE+2(%r11) # load new svc number
+ cghi %r1,NR_syscalls
+ jnl sysc_nr_ok # invalid svc number -> do svc 0
+ slag %r8,%r1,2
+ j sysc_nr_ok # restart svc
+
+#
+# _TIF_NOTIFY_RESUME is set, call do_notify_resume
+#
+sysc_notify_resume:
+ lgr %r2,%r11 # pass pointer to pt_regs
+ larl %r14,sysc_return
+ jg do_notify_resume
+
+#
+# _TIF_PER_TRAP is set, call do_per_trap
+#
+sysc_singlestep:
+ ni __TI_flags+7(%r12),255-(_TIF_SYSCALL | _TIF_PER_TRAP)
+ lgr %r2,%r11 # pass pointer to pt_regs
+ larl %r14,sysc_return
+ jg do_per_trap
+
+#
+# call tracehook_report_syscall_entry/tracehook_report_syscall_exit before
+# and after the system call
+#
+sysc_tracesys:
+ lgr %r2,%r11 # pass pointer to pt_regs
+ la %r3,0
+ llgh %r0,__PT_INT_CODE+2(%r11)
+ stg %r0,__PT_R2(%r11)
+ brasl %r14,do_syscall_trace_enter
+ lghi %r0,NR_syscalls
+ clgr %r0,%r2
+ jnh sysc_tracenogo
+ sllg %r8,%r2,2
+ lgf %r9,0(%r8,%r10)
+sysc_tracego:
+ lmg %r3,%r7,__PT_R3(%r11)
+ stg %r7,STACK_FRAME_OVERHEAD(%r15)
+ lg %r2,__PT_ORIG_GPR2(%r11)
+ basr %r14,%r9 # call sys_xxx
+ stg %r2,__PT_R2(%r11) # store return value
+sysc_tracenogo:
+ tm __TI_flags+6(%r12),_TIF_TRACE >> 8
+ jz sysc_return
+ lgr %r2,%r11 # pass pointer to pt_regs
+ larl %r14,sysc_return
+ jg do_syscall_trace_exit
+
+#
+# a new process exits the kernel with ret_from_fork
+#
+ENTRY(ret_from_fork)
+ la %r11,STACK_FRAME_OVERHEAD(%r15)
+ lg %r12,__LC_THREAD_INFO
+ tm __PT_PSW+1(%r11),0x01 # forking a kernel thread ?
+ jo 0f
+ stg %r15,__PT_R15(%r11) # store stack pointer for new kthread
+0: brasl %r14,schedule_tail
+ TRACE_IRQS_ON
+ ssm __LC_SVC_NEW_PSW # reenable interrupts
+ j sysc_tracenogo
+
+#
+# kernel_execve function needs to deal with pt_regs that is not
+# at the usual place
+#
+ENTRY(kernel_execve)
+ stmg %r12,%r15,96(%r15)
+ lgr %r14,%r15
+ aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ stg %r14,__SF_BACKCHAIN(%r15)
+ la %r12,STACK_FRAME_OVERHEAD(%r15)
+ xc 0(__PT_SIZE,%r12),0(%r12)
+ lgr %r5,%r12
+ brasl %r14,do_execve
+ ltgfr %r2,%r2
+ je 0f
+ aghi %r15,(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ lmg %r12,%r15,96(%r15)
+ br %r14
+ # execve succeeded.
+0: ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
+ lg %r15,__LC_KERNEL_STACK # load ksp
+ aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ la %r11,STACK_FRAME_OVERHEAD(%r15)
+ mvc 0(__PT_SIZE,%r11),0(%r12) # copy pt_regs
+ lg %r12,__LC_THREAD_INFO
+ xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
+ ssm __LC_SVC_NEW_PSW # reenable interrupts
+ brasl %r14,execve_tail
+ j sysc_return
+
+/*
+ * Program check handler routine
+ */
+
+ENTRY(pgm_check_handler)
+ stpt __LC_SYNC_ENTER_TIMER
+ stmg %r8,%r15,__LC_SAVE_AREA_SYNC
+ lg %r10,__LC_LAST_BREAK
+ lg %r12,__LC_THREAD_INFO
+ larl %r13,system_call
+ lmg %r8,%r9,__LC_PGM_OLD_PSW
+ HANDLE_SIE_INTERCEPT %r14
+ tmhh %r8,0x0001 # test problem state bit
+ jnz 1f # -> fault in user space
+ tmhh %r8,0x4000 # PER bit set in old PSW ?
+ jnz 0f # -> enabled, can't be a double fault
+ tm __LC_PGM_ILC+3,0x80 # check for per exception
+ jnz pgm_svcper # -> single stepped svc
+0: CHECK_STACK STACK_SIZE,__LC_SAVE_AREA_SYNC
+ j 2f
+1: UPDATE_VTIME %r14,__LC_SYNC_ENTER_TIMER
+ LAST_BREAK %r14
+ lg %r15,__LC_KERNEL_STACK
+2: aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ la %r11,STACK_FRAME_OVERHEAD(%r15)
+ stmg %r0,%r7,__PT_R0(%r11)
+ mvc __PT_R8(64,%r11),__LC_SAVE_AREA_SYNC
+ stmg %r8,%r9,__PT_PSW(%r11)
+ mvc __PT_INT_CODE(4,%r11),__LC_PGM_ILC
+ mvc __PT_INT_PARM_LONG(8,%r11),__LC_TRANS_EXC_CODE
+ stg %r10,__PT_ARGS(%r11)
+ tm __LC_PGM_ILC+3,0x80 # check for per exception
+ jz 0f
+ lg %r1,__TI_task(%r12)
+ tmhh %r8,0x0001 # kernel per event ?
+ jz pgm_kprobe
+ oi __TI_flags+7(%r12),_TIF_PER_TRAP
+ mvc __THREAD_per_address(8,%r1),__LC_PER_ADDRESS
+ mvc __THREAD_per_cause(2,%r1),__LC_PER_CAUSE
+ mvc __THREAD_per_paid(1,%r1),__LC_PER_PAID
+0: REENABLE_IRQS
+ xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
+ larl %r1,pgm_check_table
+ llgh %r10,__PT_INT_CODE+2(%r11)
+ nill %r10,0x007f
+ sll %r10,3
+ je sysc_return
+ lg %r1,0(%r10,%r1) # load address of handler routine
+ lgr %r2,%r11 # pass pointer to pt_regs
+ basr %r14,%r1 # branch to interrupt-handler
+ j sysc_return
+
+#
+# PER event in supervisor state, must be kprobes
+#
+pgm_kprobe:
+ REENABLE_IRQS
+ xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
+ lgr %r2,%r11 # pass pointer to pt_regs
+ brasl %r14,do_per_trap
+ j sysc_return
+
+#
+# single stepped system call
+#
+pgm_svcper:
+ oi __TI_flags+7(%r12),_TIF_PER_TRAP
+ mvc __LC_RETURN_PSW(8),__LC_SVC_NEW_PSW
+ larl %r14,sysc_per
+ stg %r14,__LC_RETURN_PSW+8
+ lpswe __LC_RETURN_PSW # branch to sysc_per and enable irqs
+
+/*
+ * IO interrupt handler routine
+ */
+ENTRY(io_int_handler)
+ stck __LC_INT_CLOCK
+ stpt __LC_ASYNC_ENTER_TIMER
+ stmg %r8,%r15,__LC_SAVE_AREA_ASYNC
+ lg %r10,__LC_LAST_BREAK
+ lg %r12,__LC_THREAD_INFO
+ larl %r13,system_call
+ lmg %r8,%r9,__LC_IO_OLD_PSW
+ HANDLE_SIE_INTERCEPT %r14
+ SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT
+ tmhh %r8,0x0001 # interrupting from user?
+ jz io_skip
+ UPDATE_VTIME %r14,__LC_ASYNC_ENTER_TIMER
+ LAST_BREAK %r14
+io_skip:
+ stmg %r0,%r7,__PT_R0(%r11)
+ mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC
+ stmg %r8,%r9,__PT_PSW(%r11)
+ TRACE_IRQS_OFF
+ xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
+ lgr %r2,%r11 # pass pointer to pt_regs
+ brasl %r14,do_IRQ
+io_return:
+ LOCKDEP_SYS_EXIT
+ TRACE_IRQS_ON
+io_tif:
+ tm __TI_flags+7(%r12),_TIF_WORK_INT
+ jnz io_work # there is work to do (signals etc.)
+io_restore:
+ lg %r14,__LC_VDSO_PER_CPU
+ lmg %r0,%r10,__PT_R0(%r11)
+ mvc __LC_RETURN_PSW(16),__PT_PSW(%r11)
+ stpt __LC_EXIT_TIMER
+ mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER
+ lmg %r11,%r15,__PT_R11(%r11)
+ lpswe __LC_RETURN_PSW
+io_done:
+
+#
+# There is work todo, find out in which context we have been interrupted:
+# 1) if we return to user space we can do all _TIF_WORK_INT work
+# 2) if we return to kernel code and kvm is enabled check if we need to
+# modify the psw to leave SIE
+# 3) if we return to kernel code and preemptive scheduling is enabled check
+# the preemption counter and if it is zero call preempt_schedule_irq
+# Before any work can be done, a switch to the kernel stack is required.
+#
+io_work:
+ tm __PT_PSW+1(%r11),0x01 # returning to user ?
+ jo io_work_user # yes -> do resched & signal
+#ifdef CONFIG_PREEMPT
+ # check for preemptive scheduling
+ icm %r0,15,__TI_precount(%r12)
+ jnz io_restore # preemption is disabled
+ tm __TI_flags+7(%r12),_TIF_NEED_RESCHED
+ jno io_restore
+ # switch to kernel stack
+ lg %r1,__PT_R15(%r11)
+ aghi %r1,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ mvc STACK_FRAME_OVERHEAD(__PT_SIZE,%r1),0(%r11)
+ xc __SF_BACKCHAIN(8,%r1),__SF_BACKCHAIN(%r1)
+ la %r11,STACK_FRAME_OVERHEAD(%r1)
+ lgr %r15,%r1
+ # TRACE_IRQS_ON already done at io_return, call
+ # TRACE_IRQS_OFF to keep things symmetrical
+ TRACE_IRQS_OFF
+ brasl %r14,preempt_schedule_irq
+ j io_return
+#else
+ j io_restore
+#endif
+
+#
+# Need to do work before returning to userspace, switch to kernel stack
+#
+io_work_user:
+ lg %r1,__LC_KERNEL_STACK
+ aghi %r1,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ mvc STACK_FRAME_OVERHEAD(__PT_SIZE,%r1),0(%r11)
+ xc __SF_BACKCHAIN(8,%r1),__SF_BACKCHAIN(%r1)
+ la %r11,STACK_FRAME_OVERHEAD(%r1)
+ lgr %r15,%r1
+
+#
+# One of the work bits is on. Find out which one.
+# Checked are: _TIF_SIGPENDING, _TIF_NOTIFY_RESUME, _TIF_NEED_RESCHED
+# and _TIF_MCCK_PENDING
+#
+io_work_tif:
+ tm __TI_flags+7(%r12),_TIF_MCCK_PENDING
+ jo io_mcck_pending
+ tm __TI_flags+7(%r12),_TIF_NEED_RESCHED
+ jo io_reschedule
+ tm __TI_flags+7(%r12),_TIF_SIGPENDING
+ jo io_sigpending
+ tm __TI_flags+7(%r12),_TIF_NOTIFY_RESUME
+ jo io_notify_resume
+ j io_return # beware of critical section cleanup
+
+#
+# _TIF_MCCK_PENDING is set, call handler
+#
+io_mcck_pending:
+ # TRACE_IRQS_ON already done at io_return
+ brasl %r14,s390_handle_mcck # TIF bit will be cleared by handler
+ TRACE_IRQS_OFF
+ j io_return
+
+#
+# _TIF_NEED_RESCHED is set, call schedule
+#
+io_reschedule:
+ # TRACE_IRQS_ON already done at io_return
+ ssm __LC_SVC_NEW_PSW # reenable interrupts
+ brasl %r14,schedule # call scheduler
+ ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
+ TRACE_IRQS_OFF
+ j io_return
+
+#
+# _TIF_SIGPENDING or is set, call do_signal
+#
+io_sigpending:
+ # TRACE_IRQS_ON already done at io_return
+ ssm __LC_SVC_NEW_PSW # reenable interrupts
+ lgr %r2,%r11 # pass pointer to pt_regs
+ brasl %r14,do_signal
+ ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
+ TRACE_IRQS_OFF
+ j io_return
+
+#
+# _TIF_NOTIFY_RESUME or is set, call do_notify_resume
+#
+io_notify_resume:
+ # TRACE_IRQS_ON already done at io_return
+ ssm __LC_SVC_NEW_PSW # reenable interrupts
+ lgr %r2,%r11 # pass pointer to pt_regs
+ brasl %r14,do_notify_resume
+ ssm __LC_PGM_NEW_PSW # disable I/O and ext. interrupts
+ TRACE_IRQS_OFF
+ j io_return
+
+/*
+ * External interrupt handler routine
+ */
+ENTRY(ext_int_handler)
+ stck __LC_INT_CLOCK
+ stpt __LC_ASYNC_ENTER_TIMER
+ stmg %r8,%r15,__LC_SAVE_AREA_ASYNC
+ lg %r10,__LC_LAST_BREAK
+ lg %r12,__LC_THREAD_INFO
+ larl %r13,system_call
+ lmg %r8,%r9,__LC_EXT_OLD_PSW
+ HANDLE_SIE_INTERCEPT %r14
+ SWITCH_ASYNC __LC_SAVE_AREA_ASYNC,__LC_ASYNC_STACK,STACK_SHIFT
+ tmhh %r8,0x0001 # interrupting from user ?
+ jz ext_skip
+ UPDATE_VTIME %r14,__LC_ASYNC_ENTER_TIMER
+ LAST_BREAK %r14
+ext_skip:
+ stmg %r0,%r7,__PT_R0(%r11)
+ mvc __PT_R8(64,%r11),__LC_SAVE_AREA_ASYNC
+ stmg %r8,%r9,__PT_PSW(%r11)
+ TRACE_IRQS_OFF
+ lghi %r1,4096
+ lgr %r2,%r11 # pass pointer to pt_regs
+ llgf %r3,__LC_EXT_CPU_ADDR # get cpu address + interruption code
+ llgf %r4,__LC_EXT_PARAMS # get external parameter
+ lg %r5,__LC_EXT_PARAMS2-4096(%r1) # get 64 bit external parameter
+ brasl %r14,do_extint
+ j io_return
+
+/*
+ * Load idle PSW. The second "half" of this function is in cleanup_idle.
+ */
+ENTRY(psw_idle)
+ stg %r4,__SF_EMPTY(%r15)
+ larl %r1,psw_idle_lpsw+4
+ stg %r1,__SF_EMPTY+8(%r15)
+ larl %r1,.Lvtimer_max
+ stck __IDLE_ENTER(%r2)
+ ltr %r5,%r5
+ stpt __VQ_IDLE_ENTER(%r3)
+ jz psw_idle_lpsw
+ spt 0(%r1)
+psw_idle_lpsw:
+ lpswe __SF_EMPTY(%r15)
+ br %r14
+psw_idle_end:
+
+__critical_end:
+
+/*
+ * Machine check handler routines
+ */
+ENTRY(mcck_int_handler)
+ stck __LC_MCCK_CLOCK
+ la %r1,4095 # revalidate r1
+ spt __LC_CPU_TIMER_SAVE_AREA-4095(%r1) # revalidate cpu timer
+ lmg %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)# revalidate gprs
+ lg %r10,__LC_LAST_BREAK
+ lg %r12,__LC_THREAD_INFO
+ larl %r13,system_call
+ lmg %r8,%r9,__LC_MCK_OLD_PSW
+ HANDLE_SIE_INTERCEPT %r14
+ tm __LC_MCCK_CODE,0x80 # system damage?
+ jo mcck_panic # yes -> rest of mcck code invalid
+ lghi %r14,__LC_CPU_TIMER_SAVE_AREA
+ mvc __LC_MCCK_ENTER_TIMER(8),0(%r14)
+ tm __LC_MCCK_CODE+5,0x02 # stored cpu timer value valid?
+ jo 3f
+ la %r14,__LC_SYNC_ENTER_TIMER
+ clc 0(8,%r14),__LC_ASYNC_ENTER_TIMER
+ jl 0f
+ la %r14,__LC_ASYNC_ENTER_TIMER
+0: clc 0(8,%r14),__LC_EXIT_TIMER
+ jl 1f
+ la %r14,__LC_EXIT_TIMER
+1: clc 0(8,%r14),__LC_LAST_UPDATE_TIMER
+ jl 2f
+ la %r14,__LC_LAST_UPDATE_TIMER
+2: spt 0(%r14)
+ mvc __LC_MCCK_ENTER_TIMER(8),0(%r14)
+3: tm __LC_MCCK_CODE+2,0x09 # mwp + ia of old psw valid?
+ jno mcck_panic # no -> skip cleanup critical
+ SWITCH_ASYNC __LC_GPREGS_SAVE_AREA+64,__LC_PANIC_STACK,PAGE_SHIFT
+ tm %r8,0x0001 # interrupting from user ?
+ jz mcck_skip
+ UPDATE_VTIME %r14,__LC_MCCK_ENTER_TIMER
+ LAST_BREAK %r14
+mcck_skip:
+ lghi %r14,__LC_GPREGS_SAVE_AREA
+ mvc __PT_R0(128,%r11),0(%r14)
+ stmg %r8,%r9,__PT_PSW(%r11)
+ xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
+ lgr %r2,%r11 # pass pointer to pt_regs
+ brasl %r14,s390_do_machine_check
+ tm __PT_PSW+1(%r11),0x01 # returning to user ?
+ jno mcck_return
+ lg %r1,__LC_KERNEL_STACK # switch to kernel stack
+ aghi %r1,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ mvc STACK_FRAME_OVERHEAD(__PT_SIZE,%r1),0(%r11)
+ xc __SF_BACKCHAIN(8,%r1),__SF_BACKCHAIN(%r1)
+ la %r11,STACK_FRAME_OVERHEAD(%r1)
+ lgr %r15,%r1
+ ssm __LC_PGM_NEW_PSW # turn dat on, keep irqs off
+ tm __TI_flags+7(%r12),_TIF_MCCK_PENDING
+ jno mcck_return
+ TRACE_IRQS_OFF
+ brasl %r14,s390_handle_mcck
+ TRACE_IRQS_ON
+mcck_return:
+ lg %r14,__LC_VDSO_PER_CPU
+ lmg %r0,%r10,__PT_R0(%r11)
+ mvc __LC_RETURN_MCCK_PSW(16),__PT_PSW(%r11) # move return PSW
+ tm __LC_RETURN_MCCK_PSW+1,0x01 # returning to user ?
+ jno 0f
+ stpt __LC_EXIT_TIMER
+ mvc __VDSO_ECTG_BASE(16,%r14),__LC_EXIT_TIMER
+0: lmg %r11,%r15,__PT_R11(%r11)
+ lpswe __LC_RETURN_MCCK_PSW
+
+mcck_panic:
+ lg %r14,__LC_PANIC_STACK
+ slgr %r14,%r15
+ srag %r14,%r14,PAGE_SHIFT
+ jz 0f
+ lg %r15,__LC_PANIC_STACK
+0: aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
+ j mcck_skip
+
+#
+# PSW restart interrupt handler
+#
+ENTRY(restart_int_handler)
+ stg %r15,__LC_SAVE_AREA_RESTART
+ lg %r15,__LC_RESTART_STACK
+ aghi %r15,-__PT_SIZE # create pt_regs on stack
+ xc 0(__PT_SIZE,%r15),0(%r15)
+ stmg %r0,%r14,__PT_R0(%r15)
+ mvc __PT_R15(8,%r15),__LC_SAVE_AREA_RESTART
+ mvc __PT_PSW(16,%r15),__LC_RST_OLD_PSW # store restart old psw
+ aghi %r15,-STACK_FRAME_OVERHEAD # create stack frame on stack
+ xc 0(STACK_FRAME_OVERHEAD,%r15),0(%r15)
+ lmg %r1,%r3,__LC_RESTART_FN # load fn, parm & source cpu
+ ltgr %r3,%r3 # test source cpu address
+ jm 1f # negative -> skip source stop
+0: sigp %r4,%r3,1 # sigp sense to source cpu
+ brc 10,0b # wait for status stored
+1: basr %r14,%r1 # call function
+ stap __SF_EMPTY(%r15) # store cpu address
+ llgh %r3,__SF_EMPTY(%r15)
+2: sigp %r4,%r3,5 # sigp stop to current cpu
+ brc 2,2b
+3: j 3b
+
+ .section .kprobes.text, "ax"
+
+#ifdef CONFIG_CHECK_STACK
+/*
+ * The synchronous or the asynchronous stack overflowed. We are dead.
+ * No need to properly save the registers, we are going to panic anyway.
+ * Setup a pt_regs so that show_trace can provide a good call trace.
+ */
+stack_overflow:
+ lg %r11,__LC_PANIC_STACK # change to panic stack
+ aghi %r11,-__PT_SIZE # create pt_regs
+ stmg %r0,%r7,__PT_R0(%r11)
+ stmg %r8,%r9,__PT_PSW(%r11)
+ mvc __PT_R8(64,%r11),0(%r14)
+ stg %r10,__PT_ORIG_GPR2(%r11) # store last break to orig_gpr2
+ lgr %r15,%r11
+ aghi %r15,-STACK_FRAME_OVERHEAD
+ xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
+ lgr %r2,%r11 # pass pointer to pt_regs
+ jg kernel_stack_overflow
+#endif
+
+ .align 8
+cleanup_table:
+ .quad system_call
+ .quad sysc_do_svc
+ .quad sysc_tif
+ .quad sysc_restore
+ .quad sysc_done
+ .quad io_tif
+ .quad io_restore
+ .quad io_done
+ .quad psw_idle
+ .quad psw_idle_end
+
+cleanup_critical:
+ clg %r9,BASED(cleanup_table) # system_call
+ jl 0f
+ clg %r9,BASED(cleanup_table+8) # sysc_do_svc
+ jl cleanup_system_call
+ clg %r9,BASED(cleanup_table+16) # sysc_tif
+ jl 0f
+ clg %r9,BASED(cleanup_table+24) # sysc_restore
+ jl cleanup_sysc_tif
+ clg %r9,BASED(cleanup_table+32) # sysc_done
+ jl cleanup_sysc_restore
+ clg %r9,BASED(cleanup_table+40) # io_tif
+ jl 0f
+ clg %r9,BASED(cleanup_table+48) # io_restore
+ jl cleanup_io_tif
+ clg %r9,BASED(cleanup_table+56) # io_done
+ jl cleanup_io_restore
+ clg %r9,BASED(cleanup_table+64) # psw_idle
+ jl 0f
+ clg %r9,BASED(cleanup_table+72) # psw_idle_end
+ jl cleanup_idle
+0: br %r14
+
+
+cleanup_system_call:
+ # check if stpt has been executed
+ clg %r9,BASED(cleanup_system_call_insn)
+ jh 0f
+ mvc __LC_SYNC_ENTER_TIMER(8),__LC_ASYNC_ENTER_TIMER
+ cghi %r11,__LC_SAVE_AREA_ASYNC
+ je 0f
+ mvc __LC_SYNC_ENTER_TIMER(8),__LC_MCCK_ENTER_TIMER
+0: # check if stmg has been executed
+ clg %r9,BASED(cleanup_system_call_insn+8)
+ jh 0f
+ mvc __LC_SAVE_AREA_SYNC(64),0(%r11)
+0: # check if base register setup + TIF bit load has been done
+ clg %r9,BASED(cleanup_system_call_insn+16)
+ jhe 0f
+ # set up saved registers r10 and r12
+ stg %r10,16(%r11) # r10 last break
+ stg %r12,32(%r11) # r12 thread-info pointer
+0: # check if the user time update has been done
+ clg %r9,BASED(cleanup_system_call_insn+24)
+ jh 0f
+ lg %r15,__LC_EXIT_TIMER
+ slg %r15,__LC_SYNC_ENTER_TIMER
+ alg %r15,__LC_USER_TIMER
+ stg %r15,__LC_USER_TIMER
+0: # check if the system time update has been done
+ clg %r9,BASED(cleanup_system_call_insn+32)
+ jh 0f
+ lg %r15,__LC_LAST_UPDATE_TIMER
+ slg %r15,__LC_EXIT_TIMER
+ alg %r15,__LC_SYSTEM_TIMER
+ stg %r15,__LC_SYSTEM_TIMER
+0: # update accounting time stamp
+ mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER
+ # do LAST_BREAK
+ lg %r9,16(%r11)
+ srag %r9,%r9,23
+ jz 0f
+ mvc __TI_last_break(8,%r12),16(%r11)
+0: # set up saved register r11
+ lg %r15,__LC_KERNEL_STACK
+ aghi %r15,-__PT_SIZE
+ stg %r15,24(%r11) # r11 pt_regs pointer
+ # fill pt_regs
+ mvc __PT_R8(64,%r15),__LC_SAVE_AREA_SYNC
+ stmg %r0,%r7,__PT_R0(%r15)
+ mvc __PT_PSW(16,%r15),__LC_SVC_OLD_PSW
+ mvc __PT_INT_CODE(4,%r15),__LC_SVC_ILC
+ # setup saved register r15
+ aghi %r15,-STACK_FRAME_OVERHEAD
+ stg %r15,56(%r11) # r15 stack pointer
+ # set new psw address and exit
+ larl %r9,sysc_do_svc
+ br %r14
+cleanup_system_call_insn:
+ .quad system_call
+ .quad sysc_stmg
+ .quad sysc_per
+ .quad sysc_vtime+18
+ .quad sysc_vtime+42
+
+cleanup_sysc_tif:
+ larl %r9,sysc_tif
+ br %r14
+
+cleanup_sysc_restore:
+ clg %r9,BASED(cleanup_sysc_restore_insn)
+ je 0f
+ lg %r9,24(%r11) # get saved pointer to pt_regs
+ mvc __LC_RETURN_PSW(16),__PT_PSW(%r9)
+ mvc 0(64,%r11),__PT_R8(%r9)
+ lmg %r0,%r7,__PT_R0(%r9)
+0: lmg %r8,%r9,__LC_RETURN_PSW
+ br %r14
+cleanup_sysc_restore_insn:
+ .quad sysc_done - 4
+
+cleanup_io_tif:
+ larl %r9,io_tif
+ br %r14
+
+cleanup_io_restore:
+ clg %r9,BASED(cleanup_io_restore_insn)
+ je 0f
+ lg %r9,24(%r11) # get saved r11 pointer to pt_regs
+ mvc __LC_RETURN_PSW(16),__PT_PSW(%r9)
+ mvc 0(64,%r11),__PT_R8(%r9)
+ lmg %r0,%r7,__PT_R0(%r9)
+0: lmg %r8,%r9,__LC_RETURN_PSW
+ br %r14
+cleanup_io_restore_insn:
+ .quad io_done - 4
+
+cleanup_idle:
+ # copy interrupt clock & cpu timer
+ mvc __IDLE_EXIT(8,%r2),__LC_INT_CLOCK
+ mvc __VQ_IDLE_EXIT(8,%r3),__LC_ASYNC_ENTER_TIMER
+ cghi %r11,__LC_SAVE_AREA_ASYNC
+ je 0f
+ mvc __IDLE_EXIT(8,%r2),__LC_MCCK_CLOCK
+ mvc __VQ_IDLE_EXIT(8,%r3),__LC_MCCK_ENTER_TIMER
+0: # check if stck & stpt have been executed
+ clg %r9,BASED(cleanup_idle_insn)
+ jhe 1f
+ mvc __IDLE_ENTER(8,%r2),__IDLE_EXIT(%r2)
+ mvc __VQ_IDLE_ENTER(8,%r3),__VQ_IDLE_EXIT(%r3)
+ j 2f
+1: # check if the cpu timer has been reprogrammed
+ ltr %r5,%r5
+ jz 2f
+ spt __VQ_IDLE_ENTER(%r3)
+2: # account system time going idle
+ lg %r9,__LC_STEAL_TIMER
+ alg %r9,__IDLE_ENTER(%r2)
+ slg %r9,__LC_LAST_UPDATE_CLOCK
+ stg %r9,__LC_STEAL_TIMER
+ mvc __LC_LAST_UPDATE_CLOCK(8),__IDLE_EXIT(%r2)
+ lg %r9,__LC_SYSTEM_TIMER
+ alg %r9,__LC_LAST_UPDATE_TIMER
+ slg %r9,__VQ_IDLE_ENTER(%r3)
+ stg %r9,__LC_SYSTEM_TIMER
+ mvc __LC_LAST_UPDATE_TIMER(8),__VQ_IDLE_EXIT(%r3)
+ # prepare return psw
+ nihh %r8,0xfffd # clear wait state bit
+ lg %r9,48(%r11) # return from psw_idle
+ br %r14
+cleanup_idle_insn:
+ .quad psw_idle_lpsw
+
+/*
+ * Integer constants
+ */
+ .align 8
+.Lcritical_start:
+ .quad __critical_start
+.Lcritical_length:
+ .quad __critical_end - __critical_start
+.Lvtimer_max:
+ .quad 0x7fffffffffffffff
+
+
+#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
+/*
+ * sie64a calling convention:
+ * %r2 pointer to sie control block
+ * %r3 guest register save area
+ */
+ENTRY(sie64a)
+ stmg %r6,%r14,__SF_GPRS(%r15) # save kernel registers
+ stg %r2,__SF_EMPTY(%r15) # save control block pointer
+ stg %r3,__SF_EMPTY+8(%r15) # save guest register save area
+ xc __SF_EMPTY+16(8,%r15),__SF_EMPTY+16(%r15) # host id == 0
+ lmg %r0,%r13,0(%r3) # load guest gprs 0-13
+ lg %r14,__LC_THREAD_INFO # pointer thread_info struct
+ oi __TI_flags+6(%r14),_TIF_SIE>>8
+sie_loop:
+ lg %r14,__LC_THREAD_INFO # pointer thread_info struct
+ tm __TI_flags+7(%r14),_TIF_EXIT_SIE
+ jnz sie_exit
+ lg %r14,__LC_GMAP # get gmap pointer
+ ltgr %r14,%r14
+ jz sie_gmap
+ lctlg %c1,%c1,__GMAP_ASCE(%r14) # load primary asce
+sie_gmap:
+ lg %r14,__SF_EMPTY(%r15) # get control block pointer
+ SPP __SF_EMPTY(%r15) # set guest id
+ sie 0(%r14)
+sie_done:
+ SPP __SF_EMPTY+16(%r15) # set host id
+ lg %r14,__LC_THREAD_INFO # pointer thread_info struct
+sie_exit:
+ lctlg %c1,%c1,__LC_USER_ASCE # load primary asce
+ ni __TI_flags+6(%r14),255-(_TIF_SIE>>8)
+ lg %r14,__SF_EMPTY+8(%r15) # load guest register save area
+ stmg %r0,%r13,0(%r14) # save guest gprs 0-13
+ lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers
+ lghi %r2,0
+ br %r14
+sie_fault:
+ lctlg %c1,%c1,__LC_USER_ASCE # load primary asce
+ lg %r14,__LC_THREAD_INFO # pointer thread_info struct
+ ni __TI_flags+6(%r14),255-(_TIF_SIE>>8)
+ lg %r14,__SF_EMPTY+8(%r15) # load guest register save area
+ stmg %r0,%r13,0(%r14) # save guest gprs 0-13
+ lmg %r6,%r14,__SF_GPRS(%r15) # restore kernel registers
+ lghi %r2,-EFAULT
+ br %r14
+
+ .align 8
+.Lsie_loop:
+ .quad sie_loop
+.Lsie_length:
+ .quad sie_done - sie_loop
+.Lhost_id:
+ .quad 0
+
+ .section __ex_table,"a"
+ .quad sie_loop,sie_fault
+ .previous
+#endif
+
+ .section .rodata, "a"
+#define SYSCALL(esa,esame,emu) .long esame
+ .globl sys_call_table
+sys_call_table:
+#include "syscalls.S"
+#undef SYSCALL
+
+#ifdef CONFIG_COMPAT
+
+#define SYSCALL(esa,esame,emu) .long emu
+sys_call_table_emu:
+#include "syscalls.S"
+#undef SYSCALL
+#endif
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c
new file mode 100644
index 00000000..78bdf0e5
--- /dev/null
+++ b/arch/s390/kernel/ftrace.c
@@ -0,0 +1,198 @@
+/*
+ * Dynamic function tracer architecture backend.
+ *
+ * Copyright IBM Corp. 2009
+ *
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+
+#include <linux/hardirq.h>
+#include <linux/uaccess.h>
+#include <linux/ftrace.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/kprobes.h>
+#include <trace/syscall.h>
+#include <asm/asm-offsets.h>
+
+#ifdef CONFIG_64BIT
+#define MCOUNT_OFFSET_RET 12
+#else
+#define MCOUNT_OFFSET_RET 22
+#endif
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+
+void ftrace_disable_code(void);
+void ftrace_enable_insn(void);
+
+#ifdef CONFIG_64BIT
+/*
+ * The 64-bit mcount code looks like this:
+ * stg %r14,8(%r15) # offset 0
+ * > larl %r1,<&counter> # offset 6
+ * > brasl %r14,_mcount # offset 12
+ * lg %r14,8(%r15) # offset 18
+ * Total length is 24 bytes. The middle two instructions of the mcount
+ * block get overwritten by ftrace_make_nop / ftrace_make_call.
+ * The 64-bit enabled ftrace code block looks like this:
+ * stg %r14,8(%r15) # offset 0
+ * > lg %r1,__LC_FTRACE_FUNC # offset 6
+ * > lgr %r0,%r0 # offset 12
+ * > basr %r14,%r1 # offset 16
+ * lg %r14,8(%15) # offset 18
+ * The return points of the mcount/ftrace function have the same offset 18.
+ * The 64-bit disable ftrace code block looks like this:
+ * stg %r14,8(%r15) # offset 0
+ * > jg .+18 # offset 6
+ * > lgr %r0,%r0 # offset 12
+ * > basr %r14,%r1 # offset 16
+ * lg %r14,8(%15) # offset 18
+ * The jg instruction branches to offset 24 to skip as many instructions
+ * as possible.
+ */
+asm(
+ " .align 4\n"
+ "ftrace_disable_code:\n"
+ " jg 0f\n"
+ " lgr %r0,%r0\n"
+ " basr %r14,%r1\n"
+ "0:\n"
+ " .align 4\n"
+ "ftrace_enable_insn:\n"
+ " lg %r1,"__stringify(__LC_FTRACE_FUNC)"\n");
+
+#define FTRACE_INSN_SIZE 6
+
+#else /* CONFIG_64BIT */
+/*
+ * The 31-bit mcount code looks like this:
+ * st %r14,4(%r15) # offset 0
+ * > bras %r1,0f # offset 4
+ * > .long _mcount # offset 8
+ * > .long <&counter> # offset 12
+ * > 0: l %r14,0(%r1) # offset 16
+ * > l %r1,4(%r1) # offset 20
+ * basr %r14,%r14 # offset 24
+ * l %r14,4(%r15) # offset 26
+ * Total length is 30 bytes. The twenty bytes starting from offset 4
+ * to offset 24 get overwritten by ftrace_make_nop / ftrace_make_call.
+ * The 31-bit enabled ftrace code block looks like this:
+ * st %r14,4(%r15) # offset 0
+ * > l %r14,__LC_FTRACE_FUNC # offset 4
+ * > j 0f # offset 8
+ * > .fill 12,1,0x07 # offset 12
+ * 0: basr %r14,%r14 # offset 24
+ * l %r14,4(%r14) # offset 26
+ * The return points of the mcount/ftrace function have the same offset 26.
+ * The 31-bit disabled ftrace code block looks like this:
+ * st %r14,4(%r15) # offset 0
+ * > j .+26 # offset 4
+ * > j 0f # offset 8
+ * > .fill 12,1,0x07 # offset 12
+ * 0: basr %r14,%r14 # offset 24
+ * l %r14,4(%r14) # offset 26
+ * The j instruction branches to offset 30 to skip as many instructions
+ * as possible.
+ */
+asm(
+ " .align 4\n"
+ "ftrace_disable_code:\n"
+ " j 1f\n"
+ " j 0f\n"
+ " .fill 12,1,0x07\n"
+ "0: basr %r14,%r14\n"
+ "1:\n"
+ " .align 4\n"
+ "ftrace_enable_insn:\n"
+ " l %r14,"__stringify(__LC_FTRACE_FUNC)"\n");
+
+#define FTRACE_INSN_SIZE 4
+
+#endif /* CONFIG_64BIT */
+
+
+int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec,
+ unsigned long addr)
+{
+ if (probe_kernel_write((void *) rec->ip, ftrace_disable_code,
+ MCOUNT_INSN_SIZE))
+ return -EPERM;
+ return 0;
+}
+
+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
+{
+ if (probe_kernel_write((void *) rec->ip, ftrace_enable_insn,
+ FTRACE_INSN_SIZE))
+ return -EPERM;
+ return 0;
+}
+
+int ftrace_update_ftrace_func(ftrace_func_t func)
+{
+ return 0;
+}
+
+int __init ftrace_dyn_arch_init(void *data)
+{
+ *(unsigned long *) data = 0;
+ return 0;
+}
+
+#endif /* CONFIG_DYNAMIC_FTRACE */
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+/*
+ * Hook the return address and push it in the stack of return addresses
+ * in current thread info.
+ */
+unsigned long __kprobes prepare_ftrace_return(unsigned long parent,
+ unsigned long ip)
+{
+ struct ftrace_graph_ent trace;
+
+ if (unlikely(atomic_read(&current->tracing_graph_pause)))
+ goto out;
+ if (ftrace_push_return_trace(parent, ip, &trace.depth, 0) == -EBUSY)
+ goto out;
+ trace.func = (ip & PSW_ADDR_INSN) - MCOUNT_OFFSET_RET;
+ /* Only trace if the calling function expects to. */
+ if (!ftrace_graph_entry(&trace)) {
+ current->curr_ret_stack--;
+ goto out;
+ }
+ parent = (unsigned long) return_to_handler;
+out:
+ return parent;
+}
+
+#ifdef CONFIG_DYNAMIC_FTRACE
+/*
+ * Patch the kernel code at ftrace_graph_caller location. The instruction
+ * there is branch relative and save to prepare_ftrace_return. To disable
+ * the call to prepare_ftrace_return we patch the bras offset to point
+ * directly after the instructions. To enable the call we calculate
+ * the original offset to prepare_ftrace_return and put it back.
+ */
+int ftrace_enable_ftrace_graph_caller(void)
+{
+ unsigned short offset;
+
+ offset = ((void *) prepare_ftrace_return -
+ (void *) ftrace_graph_caller) / 2;
+ return probe_kernel_write(ftrace_graph_caller + 2,
+ &offset, sizeof(offset));
+}
+
+int ftrace_disable_ftrace_graph_caller(void)
+{
+ static unsigned short offset = 0x0002;
+
+ return probe_kernel_write(ftrace_graph_caller + 2,
+ &offset, sizeof(offset));
+}
+
+#endif /* CONFIG_DYNAMIC_FTRACE */
+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S
new file mode 100644
index 00000000..adccd908
--- /dev/null
+++ b/arch/s390/kernel/head.S
@@ -0,0 +1,571 @@
+/*
+ * Copyright IBM Corp. 1999,2010
+ *
+ * Author(s): Hartmut Penner <hp@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Rob van der Heij <rvdhei@iae.nl>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ *
+ * There are 5 different IPL methods
+ * 1) load the image directly into ram at address 0 and do an PSW restart
+ * 2) linload will load the image from address 0x10000 to memory 0x10000
+ * and start the code thru LPSW 0x0008000080010000 (VM only, deprecated)
+ * 3) generate the tape ipl header, store the generated image on a tape
+ * and ipl from it
+ * In case of SL tape you need to IPL 5 times to get past VOL1 etc
+ * 4) generate the vm reader ipl header, move the generated image to the
+ * VM reader (use option NOH!) and do a ipl from reader (VM only)
+ * 5) direct call of start by the SALIPL loader
+ * We use the cpuid to distinguish between VM and native ipl
+ * params for kernel are pushed to 0x10400 (see setup.h)
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
+#include <asm/page.h>
+
+#ifdef CONFIG_64BIT
+#define ARCH_OFFSET 4
+#else
+#define ARCH_OFFSET 0
+#endif
+
+__HEAD
+#ifndef CONFIG_IPL
+ .org 0
+ .long 0x00080000,0x80000000+startup # Just a restart PSW
+#else
+#ifdef CONFIG_IPL_TAPE
+#define IPL_BS 1024
+ .org 0
+ .long 0x00080000,0x80000000+iplstart # The first 24 bytes are loaded
+ .long 0x27000000,0x60000001 # by ipl to addresses 0-23.
+ .long 0x02000000,0x20000000+IPL_BS # (a PSW and two CCWs).
+ .long 0x00000000,0x00000000 # external old psw
+ .long 0x00000000,0x00000000 # svc old psw
+ .long 0x00000000,0x00000000 # program check old psw
+ .long 0x00000000,0x00000000 # machine check old psw
+ .long 0x00000000,0x00000000 # io old psw
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x000a0000,0x00000058 # external new psw
+ .long 0x000a0000,0x00000060 # svc new psw
+ .long 0x000a0000,0x00000068 # program check new psw
+ .long 0x000a0000,0x00000070 # machine check new psw
+ .long 0x00080000,0x80000000+.Lioint # io new psw
+
+ .org 0x100
+#
+# subroutine for loading from tape
+# Parameters:
+# R1 = device number
+# R2 = load address
+.Lloader:
+ st %r14,.Lldret
+ la %r3,.Lorbread # r3 = address of orb
+ la %r5,.Lirb # r5 = address of irb
+ st %r2,.Lccwread+4 # initialize CCW data addresses
+ lctl %c6,%c6,.Lcr6
+ slr %r2,%r2
+.Lldlp:
+ la %r6,3 # 3 retries
+.Lssch:
+ ssch 0(%r3) # load chunk of IPL_BS bytes
+ bnz .Llderr
+.Lw4end:
+ bas %r14,.Lwait4io
+ tm 8(%r5),0x82 # do we have a problem ?
+ bnz .Lrecov
+ slr %r7,%r7
+ icm %r7,3,10(%r5) # get residual count
+ lcr %r7,%r7
+ la %r7,IPL_BS(%r7) # IPL_BS-residual=#bytes read
+ ar %r2,%r7 # add to total size
+ tm 8(%r5),0x01 # found a tape mark ?
+ bnz .Ldone
+ l %r0,.Lccwread+4 # update CCW data addresses
+ ar %r0,%r7
+ st %r0,.Lccwread+4
+ b .Lldlp
+.Ldone:
+ l %r14,.Lldret
+ br %r14 # r2 contains the total size
+.Lrecov:
+ bas %r14,.Lsense # do the sensing
+ bct %r6,.Lssch # dec. retry count & branch
+ b .Llderr
+#
+# Sense subroutine
+#
+.Lsense:
+ st %r14,.Lsnsret
+ la %r7,.Lorbsense
+ ssch 0(%r7) # start sense command
+ bnz .Llderr
+ bas %r14,.Lwait4io
+ l %r14,.Lsnsret
+ tm 8(%r5),0x82 # do we have a problem ?
+ bnz .Llderr
+ br %r14
+#
+# Wait for interrupt subroutine
+#
+.Lwait4io:
+ lpsw .Lwaitpsw
+.Lioint:
+ c %r1,0xb8 # compare subchannel number
+ bne .Lwait4io
+ tsch 0(%r5)
+ slr %r0,%r0
+ tm 8(%r5),0x82 # do we have a problem ?
+ bnz .Lwtexit
+ tm 8(%r5),0x04 # got device end ?
+ bz .Lwait4io
+.Lwtexit:
+ br %r14
+.Llderr:
+ lpsw .Lcrash
+
+ .align 8
+.Lorbread:
+ .long 0x00000000,0x0080ff00,.Lccwread
+ .align 8
+.Lorbsense:
+ .long 0x00000000,0x0080ff00,.Lccwsense
+ .align 8
+.Lccwread:
+ .long 0x02200000+IPL_BS,0x00000000
+.Lccwsense:
+ .long 0x04200001,0x00000000
+.Lwaitpsw:
+ .long 0x020a0000,0x80000000+.Lioint
+
+.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+.Lcr6: .long 0xff000000
+ .align 8
+.Lcrash:.long 0x000a0000,0x00000000
+.Lldret:.long 0
+.Lsnsret: .long 0
+#endif /* CONFIG_IPL_TAPE */
+
+#ifdef CONFIG_IPL_VM
+#define IPL_BS 0x730
+ .org 0
+ .long 0x00080000,0x80000000+iplstart # The first 24 bytes are loaded
+ .long 0x02000018,0x60000050 # by ipl to addresses 0-23.
+ .long 0x02000068,0x60000050 # (a PSW and two CCWs).
+ .fill 80-24,1,0x40 # bytes 24-79 are discarded !!
+ .long 0x020000f0,0x60000050 # The next 160 byte are loaded
+ .long 0x02000140,0x60000050 # to addresses 0x18-0xb7
+ .long 0x02000190,0x60000050 # They form the continuation
+ .long 0x020001e0,0x60000050 # of the CCW program started
+ .long 0x02000230,0x60000050 # by ipl and load the range
+ .long 0x02000280,0x60000050 # 0x0f0-0x730 from the image
+ .long 0x020002d0,0x60000050 # to the range 0x0f0-0x730
+ .long 0x02000320,0x60000050 # in memory. At the end of
+ .long 0x02000370,0x60000050 # the channel program the PSW
+ .long 0x020003c0,0x60000050 # at location 0 is loaded.
+ .long 0x02000410,0x60000050 # Initial processing starts
+ .long 0x02000460,0x60000050 # at 0xf0 = iplstart.
+ .long 0x020004b0,0x60000050
+ .long 0x02000500,0x60000050
+ .long 0x02000550,0x60000050
+ .long 0x020005a0,0x60000050
+ .long 0x020005f0,0x60000050
+ .long 0x02000640,0x60000050
+ .long 0x02000690,0x60000050
+ .long 0x020006e0,0x20000050
+
+ .org 0xf0
+#
+# subroutine for loading cards from the reader
+#
+.Lloader:
+ la %r3,.Lorb # r2 = address of orb into r2
+ la %r5,.Lirb # r4 = address of irb
+ la %r6,.Lccws
+ la %r7,20
+.Linit:
+ st %r2,4(%r6) # initialize CCW data addresses
+ la %r2,0x50(%r2)
+ la %r6,8(%r6)
+ bct 7,.Linit
+
+ lctl %c6,%c6,.Lcr6 # set IO subclass mask
+ slr %r2,%r2
+.Lldlp:
+ ssch 0(%r3) # load chunk of 1600 bytes
+ bnz .Llderr
+.Lwait4irq:
+ mvc 0x78(8),.Lnewpsw # set up IO interrupt psw
+ lpsw .Lwaitpsw
+.Lioint:
+ c %r1,0xb8 # compare subchannel number
+ bne .Lwait4irq
+ tsch 0(%r5)
+
+ slr %r0,%r0
+ ic %r0,8(%r5) # get device status
+ chi %r0,8 # channel end ?
+ be .Lcont
+ chi %r0,12 # channel end + device end ?
+ be .Lcont
+
+ l %r0,4(%r5)
+ s %r0,8(%r3) # r0/8 = number of ccws executed
+ mhi %r0,10 # *10 = number of bytes in ccws
+ lh %r3,10(%r5) # get residual count
+ sr %r0,%r3 # #ccws*80-residual=#bytes read
+ ar %r2,%r0
+
+ br %r14 # r2 contains the total size
+
+.Lcont:
+ ahi %r2,0x640 # add 0x640 to total size
+ la %r6,.Lccws
+ la %r7,20
+.Lincr:
+ l %r0,4(%r6) # update CCW data addresses
+ ahi %r0,0x640
+ st %r0,4(%r6)
+ ahi %r6,8
+ bct 7,.Lincr
+
+ b .Lldlp
+.Llderr:
+ lpsw .Lcrash
+
+ .align 8
+.Lorb: .long 0x00000000,0x0080ff00,.Lccws
+.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+.Lcr6: .long 0xff000000
+.Lloadp:.long 0,0
+ .align 8
+.Lcrash:.long 0x000a0000,0x00000000
+.Lnewpsw:
+ .long 0x00080000,0x80000000+.Lioint
+.Lwaitpsw:
+ .long 0x020a0000,0x80000000+.Lioint
+
+ .align 8
+.Lccws: .rept 19
+ .long 0x02600050,0x00000000
+ .endr
+ .long 0x02200050,0x00000000
+#endif /* CONFIG_IPL_VM */
+
+iplstart:
+ lh %r1,0xb8 # test if subchannel number
+ bct %r1,.Lnoload # is valid
+ l %r1,0xb8 # load ipl subchannel number
+ la %r2,IPL_BS # load start address
+ bas %r14,.Lloader # load rest of ipl image
+ l %r12,.Lparm # pointer to parameter area
+ st %r1,IPL_DEVICE+ARCH_OFFSET-PARMAREA(%r12) # save ipl device number
+
+#
+# load parameter file from ipl device
+#
+.Lagain1:
+ l %r2,.Linitrd # ramdisk loc. is temp
+ bas %r14,.Lloader # load parameter file
+ ltr %r2,%r2 # got anything ?
+ bz .Lnopf
+ chi %r2,895
+ bnh .Lnotrunc
+ la %r2,895
+.Lnotrunc:
+ l %r4,.Linitrd
+ clc 0(3,%r4),.L_hdr # if it is HDRx
+ bz .Lagain1 # skip dataset header
+ clc 0(3,%r4),.L_eof # if it is EOFx
+ bz .Lagain1 # skip dateset trailer
+ la %r5,0(%r4,%r2)
+ lr %r3,%r2
+ la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line
+ mvc 0(256,%r3),0(%r4)
+ mvc 256(256,%r3),256(%r4)
+ mvc 512(256,%r3),512(%r4)
+ mvc 768(122,%r3),768(%r4)
+ slr %r0,%r0
+ b .Lcntlp
+.Ldelspc:
+ ic %r0,0(%r2,%r3)
+ chi %r0,0x20 # is it a space ?
+ be .Lcntlp
+ ahi %r2,1
+ b .Leolp
+.Lcntlp:
+ brct %r2,.Ldelspc
+.Leolp:
+ slr %r0,%r0
+ stc %r0,0(%r2,%r3) # terminate buffer
+.Lnopf:
+
+#
+# load ramdisk from ipl device
+#
+.Lagain2:
+ l %r2,.Linitrd # addr of ramdisk
+ st %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12)
+ bas %r14,.Lloader # load ramdisk
+ st %r2,INITRD_SIZE+ARCH_OFFSET-PARMAREA(%r12) # store size of rd
+ ltr %r2,%r2
+ bnz .Lrdcont
+ st %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) # no ramdisk found
+.Lrdcont:
+ l %r2,.Linitrd
+
+ clc 0(3,%r2),.L_hdr # skip HDRx and EOFx
+ bz .Lagain2
+ clc 0(3,%r2),.L_eof
+ bz .Lagain2
+
+#ifdef CONFIG_IPL_VM
+#
+# reset files in VM reader
+#
+ stidp __LC_SAVE_AREA_SYNC # store cpuid
+ tm __LC_SAVE_AREA_SYNC,0xff# running VM ?
+ bno .Lnoreset
+ la %r2,.Lreset
+ lhi %r3,26
+ diag %r2,%r3,8
+ la %r5,.Lirb
+ stsch 0(%r5) # check if irq is pending
+ tm 30(%r5),0x0f # by verifying if any of the
+ bnz .Lwaitforirq # activity or status control
+ tm 31(%r5),0xff # bits is set in the schib
+ bz .Lnoreset
+.Lwaitforirq:
+ mvc 0x78(8),.Lrdrnewpsw # set up IO interrupt psw
+.Lwaitrdrirq:
+ lpsw .Lrdrwaitpsw
+.Lrdrint:
+ c %r1,0xb8 # compare subchannel number
+ bne .Lwaitrdrirq
+ la %r5,.Lirb
+ tsch 0(%r5)
+.Lnoreset:
+ b .Lnoload
+
+ .align 8
+.Lrdrnewpsw:
+ .long 0x00080000,0x80000000+.Lrdrint
+.Lrdrwaitpsw:
+ .long 0x020a0000,0x80000000+.Lrdrint
+#endif
+
+#
+# everything loaded, go for it
+#
+.Lnoload:
+ l %r1,.Lstartup
+ br %r1
+
+.Linitrd:.long _end # default address of initrd
+.Lparm: .long PARMAREA
+.Lstartup: .long startup
+.Lreset:.byte 0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40
+ .byte 0xc1,0xd3,0xd3,0x40,0xd2,0xc5,0xc5,0xd7,0x40,0xd5,0xd6
+ .byte 0xc8,0xd6,0xd3,0xc4 # "change rdr all keep nohold"
+.L_eof: .long 0xc5d6c600 /* C'EOF' */
+.L_hdr: .long 0xc8c4d900 /* C'HDR' */
+
+#endif /* CONFIG_IPL */
+
+#
+# SALIPL loader support. Based on a patch by Rob van der Heij.
+# This entry point is called directly from the SALIPL loader and
+# doesn't need a builtin ipl record.
+#
+ .org 0x800
+ENTRY(start)
+ stm %r0,%r15,0x07b0 # store registers
+ basr %r12,%r0
+.base:
+ l %r11,.parm
+ l %r8,.cmd # pointer to command buffer
+
+ ltr %r9,%r9 # do we have SALIPL parameters?
+ bp .sk8x8
+
+ mvc 0(64,%r8),0x00b0 # copy saved registers
+ xc 64(240-64,%r8),0(%r8) # remainder of buffer
+ tr 0(64,%r8),.lowcase
+ b .gotr
+.sk8x8:
+ mvc 0(240,%r8),0(%r9) # copy iplparms into buffer
+.gotr:
+ slr %r0,%r0
+ st %r0,INITRD_SIZE+ARCH_OFFSET-PARMAREA(%r11)
+ st %r0,INITRD_START+ARCH_OFFSET-PARMAREA(%r11)
+ j startup # continue with startup
+.cmd: .long COMMAND_LINE # address of command line buffer
+.parm: .long PARMAREA
+.lowcase:
+ .byte 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07
+ .byte 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f
+ .byte 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17
+ .byte 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f
+ .byte 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27
+ .byte 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f
+ .byte 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37
+ .byte 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f
+ .byte 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47
+ .byte 0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f
+ .byte 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57
+ .byte 0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f
+ .byte 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67
+ .byte 0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f
+ .byte 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77
+ .byte 0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f
+
+ .byte 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87
+ .byte 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f
+ .byte 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97
+ .byte 0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f
+ .byte 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7
+ .byte 0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf
+ .byte 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7
+ .byte 0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf
+ .byte 0xc0,0x81,0x82,0x83,0x84,0x85,0x86,0x87 # .abcdefg
+ .byte 0x88,0x89,0xca,0xcb,0xcc,0xcd,0xce,0xcf # hi
+ .byte 0xd0,0x91,0x92,0x93,0x94,0x95,0x96,0x97 # .jklmnop
+ .byte 0x98,0x99,0xda,0xdb,0xdc,0xdd,0xde,0xdf # qr
+ .byte 0xe0,0xe1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7 # ..stuvwx
+ .byte 0xa8,0xa9,0xea,0xeb,0xec,0xed,0xee,0xef # yz
+ .byte 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7
+ .byte 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
+
+#
+# startup-code at 0x10000, running in absolute addressing mode
+# this is called either by the ipl loader or directly by PSW restart
+# or linload or SALIPL
+#
+ .org 0x10000
+ENTRY(startup)
+ j .Lep_startup_normal
+ .org 0x10008
+#
+# This is a list of s390 kernel entry points. At address 0x1000f the number of
+# valid entry points is stored.
+#
+# IMPORTANT: Do not change this table, it is s390 kernel ABI!
+#
+ .ascii "S390EP"
+ .byte 0x00,0x01
+#
+# kdump startup-code at 0x10010, running in 64 bit absolute addressing mode
+#
+ .org 0x10010
+ENTRY(startup_kdump)
+ j .Lep_startup_kdump
+.Lep_startup_normal:
+ basr %r13,0 # get base
+.LPG0:
+ xc 0x200(256),0x200 # partially clear lowcore
+ xc 0x300(256),0x300
+ xc 0xe00(256),0xe00
+ stck __LC_LAST_UPDATE_CLOCK
+ spt 5f-.LPG0(%r13)
+ mvc __LC_LAST_UPDATE_TIMER(8),5f-.LPG0(%r13)
+ xc __LC_STFL_FAC_LIST(8),__LC_STFL_FAC_LIST
+#ifndef CONFIG_MARCH_G5
+ # check capabilities against MARCH_{G5,Z900,Z990,Z9_109,Z10}
+ .insn s,0xb2b10000,__LC_STFL_FAC_LIST # store facility list
+ tm __LC_STFL_FAC_LIST,0x01 # stfle available ?
+ jz 0f
+ la %r0,0
+ .insn s,0xb2b00000,__LC_STFL_FAC_LIST # store facility list extended
+0: l %r0,__LC_STFL_FAC_LIST
+ n %r0,2f+8-.LPG0(%r13)
+ cl %r0,2f+8-.LPG0(%r13)
+ jne 1f
+ l %r0,__LC_STFL_FAC_LIST+4
+ n %r0,2f+12-.LPG0(%r13)
+ cl %r0,2f+12-.LPG0(%r13)
+ je 3f
+1: l %r15,.Lstack-.LPG0(%r13)
+ ahi %r15,-96
+ la %r2,.Lals_string-.LPG0(%r13)
+ l %r3,.Lsclp_print-.LPG0(%r13)
+ basr %r14,%r3
+ lpsw 2f-.LPG0(%r13) # machine type not good enough, crash
+.Lals_string:
+ .asciz "The Linux kernel requires more recent processor hardware"
+.Lsclp_print:
+ .long _sclp_print_early
+.Lstack:
+ .long 0x8000 + (1<<(PAGE_SHIFT+THREAD_ORDER))
+ .align 16
+2: .long 0x000a0000,0x8badcccc
+#if defined(CONFIG_64BIT)
+#if defined(CONFIG_MARCH_Z196)
+ .long 0xc100efe3, 0xf46c0000
+#elif defined(CONFIG_MARCH_Z10)
+ .long 0xc100efe3, 0xf0680000
+#elif defined(CONFIG_MARCH_Z9_109)
+ .long 0xc100efc3, 0x00000000
+#elif defined(CONFIG_MARCH_Z990)
+ .long 0xc0002000, 0x00000000
+#elif defined(CONFIG_MARCH_Z900)
+ .long 0xc0000000, 0x00000000
+#endif
+#else
+#if defined(CONFIG_MARCH_Z196)
+ .long 0x8100c880, 0x00000000
+#elif defined(CONFIG_MARCH_Z10)
+ .long 0x8100c880, 0x00000000
+#elif defined(CONFIG_MARCH_Z9_109)
+ .long 0x8100c880, 0x00000000
+#elif defined(CONFIG_MARCH_Z990)
+ .long 0x80002000, 0x00000000
+#elif defined(CONFIG_MARCH_Z900)
+ .long 0x80000000, 0x00000000
+#endif
+#endif
+3:
+#endif
+
+#ifdef CONFIG_64BIT
+ mvi __LC_AR_MODE_ID,1 # set esame flag
+ slr %r0,%r0 # set cpuid to zero
+ lhi %r1,2 # mode 2 = esame (dump)
+ sigp %r1,%r0,0x12 # switch to esame mode
+ sam64 # switch to 64 bit mode
+ larl %r13,4f
+ lmh %r0,%r15,0(%r13) # clear high-order half
+ jg startup_continue
+4: .fill 16,4,0x0
+#else
+ mvi __LC_AR_MODE_ID,0 # set ESA flag (mode 0)
+ l %r13,4f-.LPG0(%r13)
+ b 0(%r13)
+ .align 8
+4: .long startup_continue
+#endif
+ .align 8
+5: .long 0x7fffffff,0xffffffff
+
+#include "head_kdump.S"
+
+#
+# params at 10400 (setup.h)
+#
+ .org PARMAREA
+ .long 0,0 # IPL_DEVICE
+ .long 0,0 # INITRD_START
+ .long 0,0 # INITRD_SIZE
+ .long 0,0 # OLDMEM_BASE
+ .long 0,0 # OLDMEM_SIZE
+
+ .org COMMAND_LINE
+ .byte "root=/dev/ram0 ro"
+ .byte 0
+
+ .org 0x11000
diff --git a/arch/s390/kernel/head31.S b/arch/s390/kernel/head31.S
new file mode 100644
index 00000000..d3f1ab7d
--- /dev/null
+++ b/arch/s390/kernel/head31.S
@@ -0,0 +1,112 @@
+/*
+ * arch/s390/kernel/head31.S
+ *
+ * Copyright (C) IBM Corp. 2005,2010
+ *
+ * Author(s): Hartmut Penner <hp@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Rob van der Heij <rvdhei@iae.nl>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
+#include <asm/page.h>
+
+__HEAD
+ENTRY(startup_continue)
+ basr %r13,0 # get base
+.LPG1:
+
+ l %r1,.Lbase_cc-.LPG1(%r13)
+ mvc 0(8,%r1),__LC_LAST_UPDATE_CLOCK
+ lctl %c0,%c15,.Lctl-.LPG1(%r13) # load control registers
+ l %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area
+ # move IPL device to lowcore
+#
+# Setup stack
+#
+ l %r15,.Linittu-.LPG1(%r13)
+ st %r15,__LC_THREAD_INFO # cache thread info in lowcore
+ mvc __LC_CURRENT(4),__TI_task(%r15)
+ ahi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union+THREAD_SIZE
+ st %r15,__LC_KERNEL_STACK # set end of kernel stack
+ ahi %r15,-96
+#
+# Save ipl parameters, clear bss memory, initialize storage key for kernel pages,
+# and create a kernel NSS if the SAVESYS= parm is defined
+#
+ l %r14,.Lstartup_init-.LPG1(%r13)
+ basr %r14,%r14
+ lpsw .Lentry-.LPG1(13) # jump to _stext in primary-space,
+ # virtual and never return ...
+ .align 8
+.Lentry:.long 0x00080000,0x80000000 + _stext
+.Lctl: .long 0x04b50000 # cr0: various things
+ .long 0 # cr1: primary space segment table
+ .long .Lduct # cr2: dispatchable unit control table
+ .long 0 # cr3: instruction authorization
+ .long 0 # cr4: instruction authorization
+ .long .Lduct # cr5: primary-aste origin
+ .long 0 # cr6: I/O interrupts
+ .long 0 # cr7: secondary space segment table
+ .long 0 # cr8: access registers translation
+ .long 0 # cr9: tracing off
+ .long 0 # cr10: tracing off
+ .long 0 # cr11: tracing off
+ .long 0 # cr12: tracing off
+ .long 0 # cr13: home space segment table
+ .long 0xc0000000 # cr14: machine check handling off
+ .long 0 # cr15: linkage stack operations
+.Lmchunk:.long memory_chunk
+.Lbss_bgn: .long __bss_start
+.Lbss_end: .long _end
+.Lparmaddr: .long PARMAREA
+.Linittu: .long init_thread_union
+.Lstartup_init:
+ .long startup_init
+ .align 64
+.Lduct: .long 0,0,0,0,.Lduald,0,0,0
+ .long 0,0,0,0,0,0,0,0
+ .align 128
+.Lduald:.rept 8
+ .long 0x80000000,0,0,0 # invalid access-list entries
+ .endr
+.Lbase_cc:
+ .long sched_clock_base_cc
+
+ENTRY(_ehead)
+
+#ifdef CONFIG_SHARED_KERNEL
+ .org 0x100000 - 0x11000 # head.o ends at 0x11000
+#endif
+
+#
+# startup-code, running in absolute addressing mode
+#
+ENTRY(_stext)
+ basr %r13,0 # get base
+.LPG3:
+# check control registers
+ stctl %c0,%c15,0(%r15)
+ oi 2(%r15),0x60 # enable sigp emergency & external call
+ oi 0(%r15),0x10 # switch on low address protection
+ lctl %c0,%c15,0(%r15)
+
+#
+ lam 0,15,.Laregs-.LPG3(%r13) # load access regs needed by uaccess
+ l %r14,.Lstart-.LPG3(%r13)
+ basr %r14,%r14 # call start_kernel
+#
+# We returned from start_kernel ?!? PANIK
+#
+ basr %r13,0
+ lpsw .Ldw-.(%r13) # load disabled wait psw
+#
+ .align 8
+.Ldw: .long 0x000a0000,0x00000000
+.Lstart:.long start_kernel
+.Laregs:.long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S
new file mode 100644
index 00000000..99348c0e
--- /dev/null
+++ b/arch/s390/kernel/head64.S
@@ -0,0 +1,107 @@
+/*
+ * arch/s390/kernel/head64.S
+ *
+ * Copyright (C) IBM Corp. 1999,2010
+ *
+ * Author(s): Hartmut Penner <hp@de.ibm.com>
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Rob van der Heij <rvdhei@iae.nl>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
+#include <asm/page.h>
+
+__HEAD
+ENTRY(startup_continue)
+ larl %r1,sched_clock_base_cc
+ mvc 0(8,%r1),__LC_LAST_UPDATE_CLOCK
+ larl %r13,.LPG1 # get base
+ lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers
+ lg %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area
+ # move IPL device to lowcore
+ lghi %r0,__LC_PASTE
+ stg %r0,__LC_VDSO_PER_CPU
+#
+# Setup stack
+#
+ larl %r15,init_thread_union
+ stg %r15,__LC_THREAD_INFO # cache thread info in lowcore
+ lg %r14,__TI_task(%r15) # cache current in lowcore
+ stg %r14,__LC_CURRENT
+ aghi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union + THREAD_SIZE
+ stg %r15,__LC_KERNEL_STACK # set end of kernel stack
+ aghi %r15,-160
+#
+# Save ipl parameters, clear bss memory, initialize storage key for kernel pages,
+# and create a kernel NSS if the SAVESYS= parm is defined
+#
+ brasl %r14,startup_init
+ lpswe .Lentry-.LPG1(13) # jump to _stext in primary-space,
+ # virtual and never return ...
+ .align 16
+.LPG1:
+.Lentry:.quad 0x0000000180000000,_stext
+.Lctl: .quad 0x04040000 # cr0: AFP registers & secondary space
+ .quad 0 # cr1: primary space segment table
+ .quad .Lduct # cr2: dispatchable unit control table
+ .quad 0 # cr3: instruction authorization
+ .quad 0 # cr4: instruction authorization
+ .quad .Lduct # cr5: primary-aste origin
+ .quad 0 # cr6: I/O interrupts
+ .quad 0 # cr7: secondary space segment table
+ .quad 0 # cr8: access registers translation
+ .quad 0 # cr9: tracing off
+ .quad 0 # cr10: tracing off
+ .quad 0 # cr11: tracing off
+ .quad 0 # cr12: tracing off
+ .quad 0 # cr13: home space segment table
+ .quad 0xc0000000 # cr14: machine check handling off
+ .quad 0 # cr15: linkage stack operations
+.Lpcmsk:.quad 0x0000000180000000
+.L4malign:.quad 0xffffffffffc00000
+.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
+.Lnop: .long 0x07000700
+.Lparmaddr:
+ .quad PARMAREA
+ .align 64
+.Lduct: .long 0,0,0,0,.Lduald,0,0,0
+ .long 0,0,0,0,0,0,0,0
+ .align 128
+.Lduald:.rept 8
+ .long 0x80000000,0,0,0 # invalid access-list entries
+ .endr
+
+ENTRY(_ehead)
+
+#ifdef CONFIG_SHARED_KERNEL
+ .org 0x100000 - 0x11000 # head.o ends at 0x11000
+#endif
+
+#
+# startup-code, running in absolute addressing mode
+#
+ENTRY(_stext)
+ basr %r13,0 # get base
+.LPG3:
+# check control registers
+ stctg %c0,%c15,0(%r15)
+ oi 6(%r15),0x60 # enable sigp emergency & external call
+ oi 4(%r15),0x10 # switch on low address proctection
+ lctlg %c0,%c15,0(%r15)
+
+ lam 0,15,.Laregs-.LPG3(%r13) # load acrs needed by uaccess
+ brasl %r14,start_kernel # go to C code
+#
+# We returned from start_kernel ?!? PANIK
+#
+ basr %r13,0
+ lpswe .Ldw-.(%r13) # load disabled wait psw
+
+ .align 8
+.Ldw: .quad 0x0002000180000000,0x0000000000000000
+.Laregs:.long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
diff --git a/arch/s390/kernel/head_kdump.S b/arch/s390/kernel/head_kdump.S
new file mode 100644
index 00000000..e1ac3893
--- /dev/null
+++ b/arch/s390/kernel/head_kdump.S
@@ -0,0 +1,119 @@
+/*
+ * S390 kdump lowlevel functions (new kernel)
+ *
+ * Copyright IBM Corp. 2011
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#define DATAMOVER_ADDR 0x4000
+#define COPY_PAGE_ADDR 0x6000
+
+#ifdef CONFIG_CRASH_DUMP
+
+#
+# kdump entry (new kernel - not yet relocated)
+#
+# Note: This code has to be position independent
+#
+
+.align 2
+.Lep_startup_kdump:
+ lhi %r1,2 # mode 2 = esame (dump)
+ sigp %r1,%r0,0x12 # Switch to esame mode
+ sam64 # Switch to 64 bit addressing
+ basr %r13,0
+.Lbase:
+ larl %r2,.Lbase_addr # Check, if we have been
+ lg %r2,0(%r2) # already relocated:
+ clgr %r2,%r13 #
+ jne .Lrelocate # No : Start data mover
+ lghi %r2,0 # Yes: Start kdump kernel
+ brasl %r14,startup_kdump_relocated
+
+.Lrelocate:
+ larl %r4,startup
+ lg %r2,0x418(%r4) # Get kdump base
+ lg %r3,0x420(%r4) # Get kdump size
+
+ larl %r10,.Lcopy_start # Source of data mover
+ lghi %r8,DATAMOVER_ADDR # Target of data mover
+ mvc 0(256,%r8),0(%r10) # Copy data mover code
+
+ agr %r8,%r2 # Copy data mover to
+ mvc 0(256,%r8),0(%r10) # reserved mem
+
+ lghi %r14,DATAMOVER_ADDR # Jump to copied data mover
+ basr %r14,%r14
+.Lbase_addr:
+ .quad .Lbase
+
+#
+# kdump data mover code (runs at address DATAMOVER_ADDR)
+#
+# r2: kdump base address
+# r3: kdump size
+#
+.Lcopy_start:
+ basr %r13,0 # Base
+0:
+ lgr %r11,%r2 # Save kdump base address
+ lgr %r12,%r2
+ agr %r12,%r3 # Compute kdump end address
+
+ lghi %r5,0
+ lghi %r10,COPY_PAGE_ADDR # Load copy page address
+1:
+ mvc 0(256,%r10),0(%r5) # Copy old kernel to tmp
+ mvc 0(256,%r5),0(%r11) # Copy new kernel to old
+ mvc 0(256,%r11),0(%r10) # Copy tmp to new
+ aghi %r11,256
+ aghi %r5,256
+ clgr %r11,%r12
+ jl 1b
+
+ lg %r14,.Lstartup_kdump-0b(%r13)
+ basr %r14,%r14 # Start relocated kernel
+.Lstartup_kdump:
+ .long 0x00000000,0x00000000 + startup_kdump_relocated
+.Lcopy_end:
+
+#
+# Startup of kdump (relocated new kernel)
+#
+.align 2
+startup_kdump_relocated:
+ basr %r13,0
+0:
+ mvc 0(8,%r0),.Lrestart_psw-0b(%r13) # Setup restart PSW
+ mvc 464(16,%r0),.Lpgm_psw-0b(%r13) # Setup pgm check PSW
+ lhi %r1,1 # Start new kernel
+ diag %r1,%r1,0x308 # with diag 308
+
+.Lno_diag308: # No diag 308
+ sam31 # Switch to 31 bit addr mode
+ sr %r1,%r1 # Erase register r1
+ sr %r2,%r2 # Erase register r2
+ sigp %r1,%r2,0x12 # Switch to 31 bit arch mode
+ lpsw 0 # Start new kernel...
+.align 8
+.Lrestart_psw:
+ .long 0x00080000,0x80000000 + startup
+.Lpgm_psw:
+ .quad 0x0000000180000000,0x0000000000000000 + .Lno_diag308
+#else
+.align 2
+.Lep_startup_kdump:
+#ifdef CONFIG_64BIT
+ larl %r13,startup_kdump_crash
+ lpswe 0(%r13)
+.align 8
+startup_kdump_crash:
+ .quad 0x0002000080000000,0x0000000000000000 + startup_kdump_crash
+#else
+ basr %r13,0
+0: lpsw startup_kdump_crash-0b(%r13)
+.align 8
+startup_kdump_crash:
+ .long 0x000a0000,0x00000000 + startup_kdump_crash
+#endif /* CONFIG_64BIT */
+#endif /* CONFIG_CRASH_DUMP */
diff --git a/arch/s390/kernel/init_task.c b/arch/s390/kernel/init_task.c
new file mode 100644
index 00000000..4d1c9fb0
--- /dev/null
+++ b/arch/s390/kernel/init_task.c
@@ -0,0 +1,38 @@
+/*
+ * arch/s390/kernel/init_task.c
+ *
+ * S390 version
+ *
+ * Derived from "arch/i386/kernel/init_task.c"
+ */
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init_task.h>
+#include <linux/mqueue.h>
+
+#include <asm/uaccess.h>
+#include <asm/pgtable.h>
+
+static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
+static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand);
+/*
+ * Initial thread structure.
+ *
+ * We need to make sure that this is THREAD_SIZE aligned due to the
+ * way process stacks are handled. This is done by having a special
+ * "init_task" linker map entry..
+ */
+union thread_union init_thread_union __init_task_data =
+ { INIT_THREAD_INFO(init_task) };
+
+/*
+ * Initial task structure.
+ *
+ * All other task structs will be allocated on slabs in fork.c
+ */
+struct task_struct init_task = INIT_TASK(init_task);
+
+EXPORT_SYMBOL(init_task);
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
new file mode 100644
index 00000000..8342e65a
--- /dev/null
+++ b/arch/s390/kernel/ipl.c
@@ -0,0 +1,2069 @@
+/*
+ * arch/s390/kernel/ipl.c
+ * ipl/reipl/dump support for Linux on s390.
+ *
+ * Copyright IBM Corp. 2005,2012
+ * Author(s): Michael Holzheu <holzheu@de.ibm.com>
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Volker Sameske <sameske@de.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/ctype.h>
+#include <linux/fs.h>
+#include <linux/gfp.h>
+#include <linux/crash_dump.h>
+#include <linux/debug_locks.h>
+#include <asm/ipl.h>
+#include <asm/smp.h>
+#include <asm/setup.h>
+#include <asm/cpcmd.h>
+#include <asm/cio.h>
+#include <asm/ebcdic.h>
+#include <asm/reset.h>
+#include <asm/sclp.h>
+#include <asm/checksum.h>
+#include <asm/debug.h>
+#include <asm/os_info.h>
+#include "entry.h"
+
+#define IPL_PARM_BLOCK_VERSION 0
+
+#define IPL_UNKNOWN_STR "unknown"
+#define IPL_CCW_STR "ccw"
+#define IPL_FCP_STR "fcp"
+#define IPL_FCP_DUMP_STR "fcp_dump"
+#define IPL_NSS_STR "nss"
+
+#define DUMP_CCW_STR "ccw"
+#define DUMP_FCP_STR "fcp"
+#define DUMP_NONE_STR "none"
+
+/*
+ * Four shutdown trigger types are supported:
+ * - panic
+ * - halt
+ * - power off
+ * - reipl
+ * - restart
+ */
+#define ON_PANIC_STR "on_panic"
+#define ON_HALT_STR "on_halt"
+#define ON_POFF_STR "on_poff"
+#define ON_REIPL_STR "on_reboot"
+#define ON_RESTART_STR "on_restart"
+
+struct shutdown_action;
+struct shutdown_trigger {
+ char *name;
+ struct shutdown_action *action;
+};
+
+/*
+ * The following shutdown action types are supported:
+ */
+#define SHUTDOWN_ACTION_IPL_STR "ipl"
+#define SHUTDOWN_ACTION_REIPL_STR "reipl"
+#define SHUTDOWN_ACTION_DUMP_STR "dump"
+#define SHUTDOWN_ACTION_VMCMD_STR "vmcmd"
+#define SHUTDOWN_ACTION_STOP_STR "stop"
+#define SHUTDOWN_ACTION_DUMP_REIPL_STR "dump_reipl"
+
+struct shutdown_action {
+ char *name;
+ void (*fn) (struct shutdown_trigger *trigger);
+ int (*init) (void);
+ int init_rc;
+};
+
+static char *ipl_type_str(enum ipl_type type)
+{
+ switch (type) {
+ case IPL_TYPE_CCW:
+ return IPL_CCW_STR;
+ case IPL_TYPE_FCP:
+ return IPL_FCP_STR;
+ case IPL_TYPE_FCP_DUMP:
+ return IPL_FCP_DUMP_STR;
+ case IPL_TYPE_NSS:
+ return IPL_NSS_STR;
+ case IPL_TYPE_UNKNOWN:
+ default:
+ return IPL_UNKNOWN_STR;
+ }
+}
+
+enum dump_type {
+ DUMP_TYPE_NONE = 1,
+ DUMP_TYPE_CCW = 2,
+ DUMP_TYPE_FCP = 4,
+};
+
+static char *dump_type_str(enum dump_type type)
+{
+ switch (type) {
+ case DUMP_TYPE_NONE:
+ return DUMP_NONE_STR;
+ case DUMP_TYPE_CCW:
+ return DUMP_CCW_STR;
+ case DUMP_TYPE_FCP:
+ return DUMP_FCP_STR;
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * Must be in data section since the bss section
+ * is not cleared when these are accessed.
+ */
+static u16 ipl_devno __attribute__((__section__(".data"))) = 0;
+u32 ipl_flags __attribute__((__section__(".data"))) = 0;
+
+enum ipl_method {
+ REIPL_METHOD_CCW_CIO,
+ REIPL_METHOD_CCW_DIAG,
+ REIPL_METHOD_CCW_VM,
+ REIPL_METHOD_FCP_RO_DIAG,
+ REIPL_METHOD_FCP_RW_DIAG,
+ REIPL_METHOD_FCP_RO_VM,
+ REIPL_METHOD_FCP_DUMP,
+ REIPL_METHOD_NSS,
+ REIPL_METHOD_NSS_DIAG,
+ REIPL_METHOD_DEFAULT,
+};
+
+enum dump_method {
+ DUMP_METHOD_NONE,
+ DUMP_METHOD_CCW_CIO,
+ DUMP_METHOD_CCW_DIAG,
+ DUMP_METHOD_CCW_VM,
+ DUMP_METHOD_FCP_DIAG,
+};
+
+static int diag308_set_works = 0;
+
+static struct ipl_parameter_block ipl_block;
+
+static int reipl_capabilities = IPL_TYPE_UNKNOWN;
+
+static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
+static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT;
+static struct ipl_parameter_block *reipl_block_fcp;
+static struct ipl_parameter_block *reipl_block_ccw;
+static struct ipl_parameter_block *reipl_block_nss;
+static struct ipl_parameter_block *reipl_block_actual;
+
+static int dump_capabilities = DUMP_TYPE_NONE;
+static enum dump_type dump_type = DUMP_TYPE_NONE;
+static enum dump_method dump_method = DUMP_METHOD_NONE;
+static struct ipl_parameter_block *dump_block_fcp;
+static struct ipl_parameter_block *dump_block_ccw;
+
+static struct sclp_ipl_info sclp_ipl_info;
+
+int diag308(unsigned long subcode, void *addr)
+{
+ register unsigned long _addr asm("0") = (unsigned long) addr;
+ register unsigned long _rc asm("1") = 0;
+
+ asm volatile(
+ " diag %0,%2,0x308\n"
+ "0:\n"
+ EX_TABLE(0b,0b)
+ : "+d" (_addr), "+d" (_rc)
+ : "d" (subcode) : "cc", "memory");
+ return _rc;
+}
+EXPORT_SYMBOL_GPL(diag308);
+
+/* SYSFS */
+
+#define DEFINE_IPL_ATTR_RO(_prefix, _name, _format, _value) \
+static ssize_t sys_##_prefix##_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ char *page) \
+{ \
+ return sprintf(page, _format, _value); \
+} \
+static struct kobj_attribute sys_##_prefix##_##_name##_attr = \
+ __ATTR(_name, S_IRUGO, sys_##_prefix##_##_name##_show, NULL);
+
+#define DEFINE_IPL_ATTR_RW(_prefix, _name, _fmt_out, _fmt_in, _value) \
+static ssize_t sys_##_prefix##_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ char *page) \
+{ \
+ return sprintf(page, _fmt_out, \
+ (unsigned long long) _value); \
+} \
+static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ unsigned long long value; \
+ if (sscanf(buf, _fmt_in, &value) != 1) \
+ return -EINVAL; \
+ _value = value; \
+ return len; \
+} \
+static struct kobj_attribute sys_##_prefix##_##_name##_attr = \
+ __ATTR(_name,(S_IRUGO | S_IWUSR), \
+ sys_##_prefix##_##_name##_show, \
+ sys_##_prefix##_##_name##_store);
+
+#define DEFINE_IPL_ATTR_STR_RW(_prefix, _name, _fmt_out, _fmt_in, _value)\
+static ssize_t sys_##_prefix##_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ char *page) \
+{ \
+ return sprintf(page, _fmt_out, _value); \
+} \
+static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ const char *buf, size_t len) \
+{ \
+ strncpy(_value, buf, sizeof(_value) - 1); \
+ strim(_value); \
+ return len; \
+} \
+static struct kobj_attribute sys_##_prefix##_##_name##_attr = \
+ __ATTR(_name,(S_IRUGO | S_IWUSR), \
+ sys_##_prefix##_##_name##_show, \
+ sys_##_prefix##_##_name##_store);
+
+static void make_attrs_ro(struct attribute **attrs)
+{
+ while (*attrs) {
+ (*attrs)->mode = S_IRUGO;
+ attrs++;
+ }
+}
+
+/*
+ * ipl section
+ */
+
+static __init enum ipl_type get_ipl_type(void)
+{
+ struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
+
+ if (ipl_flags & IPL_NSS_VALID)
+ return IPL_TYPE_NSS;
+ if (!(ipl_flags & IPL_DEVNO_VALID))
+ return IPL_TYPE_UNKNOWN;
+ if (!(ipl_flags & IPL_PARMBLOCK_VALID))
+ return IPL_TYPE_CCW;
+ if (ipl->hdr.version > IPL_MAX_SUPPORTED_VERSION)
+ return IPL_TYPE_UNKNOWN;
+ if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP)
+ return IPL_TYPE_UNKNOWN;
+ if (ipl->ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP)
+ return IPL_TYPE_FCP_DUMP;
+ return IPL_TYPE_FCP;
+}
+
+struct ipl_info ipl_info;
+EXPORT_SYMBOL_GPL(ipl_info);
+
+static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *page)
+{
+ return sprintf(page, "%s\n", ipl_type_str(ipl_info.type));
+}
+
+static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
+
+/* VM IPL PARM routines */
+static size_t reipl_get_ascii_vmparm(char *dest, size_t size,
+ const struct ipl_parameter_block *ipb)
+{
+ int i;
+ size_t len;
+ char has_lowercase = 0;
+
+ len = 0;
+ if ((ipb->ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID) &&
+ (ipb->ipl_info.ccw.vm_parm_len > 0)) {
+
+ len = min_t(size_t, size - 1, ipb->ipl_info.ccw.vm_parm_len);
+ memcpy(dest, ipb->ipl_info.ccw.vm_parm, len);
+ /* If at least one character is lowercase, we assume mixed
+ * case; otherwise we convert everything to lowercase.
+ */
+ for (i = 0; i < len; i++)
+ if ((dest[i] > 0x80 && dest[i] < 0x8a) || /* a-i */
+ (dest[i] > 0x90 && dest[i] < 0x9a) || /* j-r */
+ (dest[i] > 0xa1 && dest[i] < 0xaa)) { /* s-z */
+ has_lowercase = 1;
+ break;
+ }
+ if (!has_lowercase)
+ EBC_TOLOWER(dest, len);
+ EBCASC(dest, len);
+ }
+ dest[len] = 0;
+
+ return len;
+}
+
+size_t append_ipl_vmparm(char *dest, size_t size)
+{
+ size_t rc;
+
+ rc = 0;
+ if (diag308_set_works && (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_CCW))
+ rc = reipl_get_ascii_vmparm(dest, size, &ipl_block);
+ else
+ dest[0] = 0;
+ return rc;
+}
+
+static ssize_t ipl_vm_parm_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ char parm[DIAG308_VMPARM_SIZE + 1] = {};
+
+ append_ipl_vmparm(parm, sizeof(parm));
+ return sprintf(page, "%s\n", parm);
+}
+
+static size_t scpdata_length(const char* buf, size_t count)
+{
+ while (count) {
+ if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
+ break;
+ count--;
+ }
+ return count;
+}
+
+static size_t reipl_append_ascii_scpdata(char *dest, size_t size,
+ const struct ipl_parameter_block *ipb)
+{
+ size_t count;
+ size_t i;
+ int has_lowercase;
+
+ count = min(size - 1, scpdata_length(ipb->ipl_info.fcp.scp_data,
+ ipb->ipl_info.fcp.scp_data_len));
+ if (!count)
+ goto out;
+
+ has_lowercase = 0;
+ for (i = 0; i < count; i++) {
+ if (!isascii(ipb->ipl_info.fcp.scp_data[i])) {
+ count = 0;
+ goto out;
+ }
+ if (!has_lowercase && islower(ipb->ipl_info.fcp.scp_data[i]))
+ has_lowercase = 1;
+ }
+
+ if (has_lowercase)
+ memcpy(dest, ipb->ipl_info.fcp.scp_data, count);
+ else
+ for (i = 0; i < count; i++)
+ dest[i] = tolower(ipb->ipl_info.fcp.scp_data[i]);
+out:
+ dest[count] = '\0';
+ return count;
+}
+
+size_t append_ipl_scpdata(char *dest, size_t len)
+{
+ size_t rc;
+
+ rc = 0;
+ if (ipl_block.hdr.pbt == DIAG308_IPL_TYPE_FCP)
+ rc = reipl_append_ascii_scpdata(dest, len, &ipl_block);
+ else
+ dest[0] = 0;
+ return rc;
+}
+
+
+static struct kobj_attribute sys_ipl_vm_parm_attr =
+ __ATTR(parm, S_IRUGO, ipl_vm_parm_show, NULL);
+
+static ssize_t sys_ipl_device_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
+
+ switch (ipl_info.type) {
+ case IPL_TYPE_CCW:
+ return sprintf(page, "0.0.%04x\n", ipl_devno);
+ case IPL_TYPE_FCP:
+ case IPL_TYPE_FCP_DUMP:
+ return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno);
+ default:
+ return 0;
+ }
+}
+
+static struct kobj_attribute sys_ipl_device_attr =
+ __ATTR(device, S_IRUGO, sys_ipl_device_show, NULL);
+
+static ssize_t ipl_parameter_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+ return memory_read_from_buffer(buf, count, &off, IPL_PARMBLOCK_START,
+ IPL_PARMBLOCK_SIZE);
+}
+
+static struct bin_attribute ipl_parameter_attr = {
+ .attr = {
+ .name = "binary_parameter",
+ .mode = S_IRUGO,
+ },
+ .size = PAGE_SIZE,
+ .read = &ipl_parameter_read,
+};
+
+static ssize_t ipl_scp_data_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+ unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len;
+ void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data;
+
+ return memory_read_from_buffer(buf, count, &off, scp_data, size);
+}
+
+static struct bin_attribute ipl_scp_data_attr = {
+ .attr = {
+ .name = "scp_data",
+ .mode = S_IRUGO,
+ },
+ .size = PAGE_SIZE,
+ .read = ipl_scp_data_read,
+};
+
+/* FCP ipl device attributes */
+
+DEFINE_IPL_ATTR_RO(ipl_fcp, wwpn, "0x%016llx\n", (unsigned long long)
+ IPL_PARMBLOCK_START->ipl_info.fcp.wwpn);
+DEFINE_IPL_ATTR_RO(ipl_fcp, lun, "0x%016llx\n", (unsigned long long)
+ IPL_PARMBLOCK_START->ipl_info.fcp.lun);
+DEFINE_IPL_ATTR_RO(ipl_fcp, bootprog, "%lld\n", (unsigned long long)
+ IPL_PARMBLOCK_START->ipl_info.fcp.bootprog);
+DEFINE_IPL_ATTR_RO(ipl_fcp, br_lba, "%lld\n", (unsigned long long)
+ IPL_PARMBLOCK_START->ipl_info.fcp.br_lba);
+
+static struct attribute *ipl_fcp_attrs[] = {
+ &sys_ipl_type_attr.attr,
+ &sys_ipl_device_attr.attr,
+ &sys_ipl_fcp_wwpn_attr.attr,
+ &sys_ipl_fcp_lun_attr.attr,
+ &sys_ipl_fcp_bootprog_attr.attr,
+ &sys_ipl_fcp_br_lba_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ipl_fcp_attr_group = {
+ .attrs = ipl_fcp_attrs,
+};
+
+/* CCW ipl device attributes */
+
+static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ char loadparm[LOADPARM_LEN + 1] = {};
+
+ if (!sclp_ipl_info.is_valid)
+ return sprintf(page, "#unknown#\n");
+ memcpy(loadparm, &sclp_ipl_info.loadparm, LOADPARM_LEN);
+ EBCASC(loadparm, LOADPARM_LEN);
+ strim(loadparm);
+ return sprintf(page, "%s\n", loadparm);
+}
+
+static struct kobj_attribute sys_ipl_ccw_loadparm_attr =
+ __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL);
+
+static struct attribute *ipl_ccw_attrs_vm[] = {
+ &sys_ipl_type_attr.attr,
+ &sys_ipl_device_attr.attr,
+ &sys_ipl_ccw_loadparm_attr.attr,
+ &sys_ipl_vm_parm_attr.attr,
+ NULL,
+};
+
+static struct attribute *ipl_ccw_attrs_lpar[] = {
+ &sys_ipl_type_attr.attr,
+ &sys_ipl_device_attr.attr,
+ &sys_ipl_ccw_loadparm_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ipl_ccw_attr_group_vm = {
+ .attrs = ipl_ccw_attrs_vm,
+};
+
+static struct attribute_group ipl_ccw_attr_group_lpar = {
+ .attrs = ipl_ccw_attrs_lpar
+};
+
+/* NSS ipl device attributes */
+
+DEFINE_IPL_ATTR_RO(ipl_nss, name, "%s\n", kernel_nss_name);
+
+static struct attribute *ipl_nss_attrs[] = {
+ &sys_ipl_type_attr.attr,
+ &sys_ipl_nss_name_attr.attr,
+ &sys_ipl_ccw_loadparm_attr.attr,
+ &sys_ipl_vm_parm_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ipl_nss_attr_group = {
+ .attrs = ipl_nss_attrs,
+};
+
+/* UNKNOWN ipl device attributes */
+
+static struct attribute *ipl_unknown_attrs[] = {
+ &sys_ipl_type_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ipl_unknown_attr_group = {
+ .attrs = ipl_unknown_attrs,
+};
+
+static struct kset *ipl_kset;
+
+static int __init ipl_register_fcp_files(void)
+{
+ int rc;
+
+ rc = sysfs_create_group(&ipl_kset->kobj, &ipl_fcp_attr_group);
+ if (rc)
+ goto out;
+ rc = sysfs_create_bin_file(&ipl_kset->kobj, &ipl_parameter_attr);
+ if (rc)
+ goto out_ipl_parm;
+ rc = sysfs_create_bin_file(&ipl_kset->kobj, &ipl_scp_data_attr);
+ if (!rc)
+ goto out;
+
+ sysfs_remove_bin_file(&ipl_kset->kobj, &ipl_parameter_attr);
+
+out_ipl_parm:
+ sysfs_remove_group(&ipl_kset->kobj, &ipl_fcp_attr_group);
+out:
+ return rc;
+}
+
+static void __ipl_run(void *unused)
+{
+ diag308(DIAG308_IPL, NULL);
+ if (MACHINE_IS_VM)
+ __cpcmd("IPL", NULL, 0, NULL);
+ else if (ipl_info.type == IPL_TYPE_CCW)
+ reipl_ccw_dev(&ipl_info.data.ccw.dev_id);
+}
+
+static void ipl_run(struct shutdown_trigger *trigger)
+{
+ smp_call_ipl_cpu(__ipl_run, NULL);
+}
+
+static int __init ipl_init(void)
+{
+ int rc;
+
+ ipl_kset = kset_create_and_add("ipl", NULL, firmware_kobj);
+ if (!ipl_kset) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ switch (ipl_info.type) {
+ case IPL_TYPE_CCW:
+ if (MACHINE_IS_VM)
+ rc = sysfs_create_group(&ipl_kset->kobj,
+ &ipl_ccw_attr_group_vm);
+ else
+ rc = sysfs_create_group(&ipl_kset->kobj,
+ &ipl_ccw_attr_group_lpar);
+ break;
+ case IPL_TYPE_FCP:
+ case IPL_TYPE_FCP_DUMP:
+ rc = ipl_register_fcp_files();
+ break;
+ case IPL_TYPE_NSS:
+ rc = sysfs_create_group(&ipl_kset->kobj, &ipl_nss_attr_group);
+ break;
+ default:
+ rc = sysfs_create_group(&ipl_kset->kobj,
+ &ipl_unknown_attr_group);
+ break;
+ }
+out:
+ if (rc)
+ panic("ipl_init failed: rc = %i\n", rc);
+
+ return 0;
+}
+
+static struct shutdown_action __refdata ipl_action = {
+ .name = SHUTDOWN_ACTION_IPL_STR,
+ .fn = ipl_run,
+ .init = ipl_init,
+};
+
+/*
+ * reipl shutdown action: Reboot Linux on shutdown.
+ */
+
+/* VM IPL PARM attributes */
+static ssize_t reipl_generic_vmparm_show(struct ipl_parameter_block *ipb,
+ char *page)
+{
+ char vmparm[DIAG308_VMPARM_SIZE + 1] = {};
+
+ reipl_get_ascii_vmparm(vmparm, sizeof(vmparm), ipb);
+ return sprintf(page, "%s\n", vmparm);
+}
+
+static ssize_t reipl_generic_vmparm_store(struct ipl_parameter_block *ipb,
+ size_t vmparm_max,
+ const char *buf, size_t len)
+{
+ int i, ip_len;
+
+ /* ignore trailing newline */
+ ip_len = len;
+ if ((len > 0) && (buf[len - 1] == '\n'))
+ ip_len--;
+
+ if (ip_len > vmparm_max)
+ return -EINVAL;
+
+ /* parm is used to store kernel options, check for common chars */
+ for (i = 0; i < ip_len; i++)
+ if (!(isalnum(buf[i]) || isascii(buf[i]) || isprint(buf[i])))
+ return -EINVAL;
+
+ memset(ipb->ipl_info.ccw.vm_parm, 0, DIAG308_VMPARM_SIZE);
+ ipb->ipl_info.ccw.vm_parm_len = ip_len;
+ if (ip_len > 0) {
+ ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID;
+ memcpy(ipb->ipl_info.ccw.vm_parm, buf, ip_len);
+ ASCEBC(ipb->ipl_info.ccw.vm_parm, ip_len);
+ } else {
+ ipb->ipl_info.ccw.vm_flags &= ~DIAG308_VM_FLAGS_VP_VALID;
+ }
+
+ return len;
+}
+
+/* NSS wrapper */
+static ssize_t reipl_nss_vmparm_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return reipl_generic_vmparm_show(reipl_block_nss, page);
+}
+
+static ssize_t reipl_nss_vmparm_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return reipl_generic_vmparm_store(reipl_block_nss, 56, buf, len);
+}
+
+/* CCW wrapper */
+static ssize_t reipl_ccw_vmparm_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return reipl_generic_vmparm_show(reipl_block_ccw, page);
+}
+
+static ssize_t reipl_ccw_vmparm_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return reipl_generic_vmparm_store(reipl_block_ccw, 64, buf, len);
+}
+
+static struct kobj_attribute sys_reipl_nss_vmparm_attr =
+ __ATTR(parm, S_IRUGO | S_IWUSR, reipl_nss_vmparm_show,
+ reipl_nss_vmparm_store);
+static struct kobj_attribute sys_reipl_ccw_vmparm_attr =
+ __ATTR(parm, S_IRUGO | S_IWUSR, reipl_ccw_vmparm_show,
+ reipl_ccw_vmparm_store);
+
+/* FCP reipl device attributes */
+
+static ssize_t reipl_fcp_scpdata_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ size_t size = reipl_block_fcp->ipl_info.fcp.scp_data_len;
+ void *scp_data = reipl_block_fcp->ipl_info.fcp.scp_data;
+
+ return memory_read_from_buffer(buf, count, &off, scp_data, size);
+}
+
+static ssize_t reipl_fcp_scpdata_write(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr,
+ char *buf, loff_t off, size_t count)
+{
+ size_t padding;
+ size_t scpdata_len;
+
+ if (off < 0)
+ return -EINVAL;
+
+ if (off >= DIAG308_SCPDATA_SIZE)
+ return -ENOSPC;
+
+ if (count > DIAG308_SCPDATA_SIZE - off)
+ count = DIAG308_SCPDATA_SIZE - off;
+
+ memcpy(reipl_block_fcp->ipl_info.fcp.scp_data, buf + off, count);
+ scpdata_len = off + count;
+
+ if (scpdata_len % 8) {
+ padding = 8 - (scpdata_len % 8);
+ memset(reipl_block_fcp->ipl_info.fcp.scp_data + scpdata_len,
+ 0, padding);
+ scpdata_len += padding;
+ }
+
+ reipl_block_fcp->ipl_info.fcp.scp_data_len = scpdata_len;
+ reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN + scpdata_len;
+ reipl_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN + scpdata_len;
+
+ return count;
+}
+
+static struct bin_attribute sys_reipl_fcp_scp_data_attr = {
+ .attr = {
+ .name = "scp_data",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .size = PAGE_SIZE,
+ .read = reipl_fcp_scpdata_read,
+ .write = reipl_fcp_scpdata_write,
+};
+
+DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n",
+ reipl_block_fcp->ipl_info.fcp.wwpn);
+DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%016llx\n",
+ reipl_block_fcp->ipl_info.fcp.lun);
+DEFINE_IPL_ATTR_RW(reipl_fcp, bootprog, "%lld\n", "%lld\n",
+ reipl_block_fcp->ipl_info.fcp.bootprog);
+DEFINE_IPL_ATTR_RW(reipl_fcp, br_lba, "%lld\n", "%lld\n",
+ reipl_block_fcp->ipl_info.fcp.br_lba);
+DEFINE_IPL_ATTR_RW(reipl_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
+ reipl_block_fcp->ipl_info.fcp.devno);
+
+static struct attribute *reipl_fcp_attrs[] = {
+ &sys_reipl_fcp_device_attr.attr,
+ &sys_reipl_fcp_wwpn_attr.attr,
+ &sys_reipl_fcp_lun_attr.attr,
+ &sys_reipl_fcp_bootprog_attr.attr,
+ &sys_reipl_fcp_br_lba_attr.attr,
+ NULL,
+};
+
+static struct attribute_group reipl_fcp_attr_group = {
+ .attrs = reipl_fcp_attrs,
+};
+
+/* CCW reipl device attributes */
+
+DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
+ reipl_block_ccw->ipl_info.ccw.devno);
+
+static void reipl_get_ascii_loadparm(char *loadparm,
+ struct ipl_parameter_block *ibp)
+{
+ memcpy(loadparm, ibp->ipl_info.ccw.load_parm, LOADPARM_LEN);
+ EBCASC(loadparm, LOADPARM_LEN);
+ loadparm[LOADPARM_LEN] = 0;
+ strim(loadparm);
+}
+
+static ssize_t reipl_generic_loadparm_show(struct ipl_parameter_block *ipb,
+ char *page)
+{
+ char buf[LOADPARM_LEN + 1];
+
+ reipl_get_ascii_loadparm(buf, ipb);
+ return sprintf(page, "%s\n", buf);
+}
+
+static ssize_t reipl_generic_loadparm_store(struct ipl_parameter_block *ipb,
+ const char *buf, size_t len)
+{
+ int i, lp_len;
+
+ /* ignore trailing newline */
+ lp_len = len;
+ if ((len > 0) && (buf[len - 1] == '\n'))
+ lp_len--;
+ /* loadparm can have max 8 characters and must not start with a blank */
+ if ((lp_len > LOADPARM_LEN) || ((lp_len > 0) && (buf[0] == ' ')))
+ return -EINVAL;
+ /* loadparm can only contain "a-z,A-Z,0-9,SP,." */
+ for (i = 0; i < lp_len; i++) {
+ if (isalpha(buf[i]) || isdigit(buf[i]) || (buf[i] == ' ') ||
+ (buf[i] == '.'))
+ continue;
+ return -EINVAL;
+ }
+ /* initialize loadparm with blanks */
+ memset(ipb->ipl_info.ccw.load_parm, ' ', LOADPARM_LEN);
+ /* copy and convert to ebcdic */
+ memcpy(ipb->ipl_info.ccw.load_parm, buf, lp_len);
+ ASCEBC(ipb->ipl_info.ccw.load_parm, LOADPARM_LEN);
+ return len;
+}
+
+/* NSS wrapper */
+static ssize_t reipl_nss_loadparm_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return reipl_generic_loadparm_show(reipl_block_nss, page);
+}
+
+static ssize_t reipl_nss_loadparm_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return reipl_generic_loadparm_store(reipl_block_nss, buf, len);
+}
+
+/* CCW wrapper */
+static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return reipl_generic_loadparm_show(reipl_block_ccw, page);
+}
+
+static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return reipl_generic_loadparm_store(reipl_block_ccw, buf, len);
+}
+
+static struct kobj_attribute sys_reipl_ccw_loadparm_attr =
+ __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_ccw_loadparm_show,
+ reipl_ccw_loadparm_store);
+
+static struct attribute *reipl_ccw_attrs_vm[] = {
+ &sys_reipl_ccw_device_attr.attr,
+ &sys_reipl_ccw_loadparm_attr.attr,
+ &sys_reipl_ccw_vmparm_attr.attr,
+ NULL,
+};
+
+static struct attribute *reipl_ccw_attrs_lpar[] = {
+ &sys_reipl_ccw_device_attr.attr,
+ &sys_reipl_ccw_loadparm_attr.attr,
+ NULL,
+};
+
+static struct attribute_group reipl_ccw_attr_group_vm = {
+ .name = IPL_CCW_STR,
+ .attrs = reipl_ccw_attrs_vm,
+};
+
+static struct attribute_group reipl_ccw_attr_group_lpar = {
+ .name = IPL_CCW_STR,
+ .attrs = reipl_ccw_attrs_lpar,
+};
+
+
+/* NSS reipl device attributes */
+static void reipl_get_ascii_nss_name(char *dst,
+ struct ipl_parameter_block *ipb)
+{
+ memcpy(dst, ipb->ipl_info.ccw.nss_name, NSS_NAME_SIZE);
+ EBCASC(dst, NSS_NAME_SIZE);
+ dst[NSS_NAME_SIZE] = 0;
+}
+
+static ssize_t reipl_nss_name_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ char nss_name[NSS_NAME_SIZE + 1] = {};
+
+ reipl_get_ascii_nss_name(nss_name, reipl_block_nss);
+ return sprintf(page, "%s\n", nss_name);
+}
+
+static ssize_t reipl_nss_name_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ int nss_len;
+
+ /* ignore trailing newline */
+ nss_len = len;
+ if ((len > 0) && (buf[len - 1] == '\n'))
+ nss_len--;
+
+ if (nss_len > NSS_NAME_SIZE)
+ return -EINVAL;
+
+ memset(reipl_block_nss->ipl_info.ccw.nss_name, 0x40, NSS_NAME_SIZE);
+ if (nss_len > 0) {
+ reipl_block_nss->ipl_info.ccw.vm_flags |=
+ DIAG308_VM_FLAGS_NSS_VALID;
+ memcpy(reipl_block_nss->ipl_info.ccw.nss_name, buf, nss_len);
+ ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, nss_len);
+ EBC_TOUPPER(reipl_block_nss->ipl_info.ccw.nss_name, nss_len);
+ } else {
+ reipl_block_nss->ipl_info.ccw.vm_flags &=
+ ~DIAG308_VM_FLAGS_NSS_VALID;
+ }
+
+ return len;
+}
+
+static struct kobj_attribute sys_reipl_nss_name_attr =
+ __ATTR(name, S_IRUGO | S_IWUSR, reipl_nss_name_show,
+ reipl_nss_name_store);
+
+static struct kobj_attribute sys_reipl_nss_loadparm_attr =
+ __ATTR(loadparm, S_IRUGO | S_IWUSR, reipl_nss_loadparm_show,
+ reipl_nss_loadparm_store);
+
+static struct attribute *reipl_nss_attrs[] = {
+ &sys_reipl_nss_name_attr.attr,
+ &sys_reipl_nss_loadparm_attr.attr,
+ &sys_reipl_nss_vmparm_attr.attr,
+ NULL,
+};
+
+static struct attribute_group reipl_nss_attr_group = {
+ .name = IPL_NSS_STR,
+ .attrs = reipl_nss_attrs,
+};
+
+static void set_reipl_block_actual(struct ipl_parameter_block *reipl_block)
+{
+ reipl_block_actual = reipl_block;
+ os_info_entry_add(OS_INFO_REIPL_BLOCK, reipl_block_actual,
+ reipl_block->hdr.len);
+}
+
+/* reipl type */
+
+static int reipl_set_type(enum ipl_type type)
+{
+ if (!(reipl_capabilities & type))
+ return -EINVAL;
+
+ switch(type) {
+ case IPL_TYPE_CCW:
+ if (diag308_set_works)
+ reipl_method = REIPL_METHOD_CCW_DIAG;
+ else if (MACHINE_IS_VM)
+ reipl_method = REIPL_METHOD_CCW_VM;
+ else
+ reipl_method = REIPL_METHOD_CCW_CIO;
+ set_reipl_block_actual(reipl_block_ccw);
+ break;
+ case IPL_TYPE_FCP:
+ if (diag308_set_works)
+ reipl_method = REIPL_METHOD_FCP_RW_DIAG;
+ else if (MACHINE_IS_VM)
+ reipl_method = REIPL_METHOD_FCP_RO_VM;
+ else
+ reipl_method = REIPL_METHOD_FCP_RO_DIAG;
+ set_reipl_block_actual(reipl_block_fcp);
+ break;
+ case IPL_TYPE_FCP_DUMP:
+ reipl_method = REIPL_METHOD_FCP_DUMP;
+ break;
+ case IPL_TYPE_NSS:
+ if (diag308_set_works)
+ reipl_method = REIPL_METHOD_NSS_DIAG;
+ else
+ reipl_method = REIPL_METHOD_NSS;
+ set_reipl_block_actual(reipl_block_nss);
+ break;
+ case IPL_TYPE_UNKNOWN:
+ reipl_method = REIPL_METHOD_DEFAULT;
+ break;
+ default:
+ BUG();
+ }
+ reipl_type = type;
+ return 0;
+}
+
+static ssize_t reipl_type_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", ipl_type_str(reipl_type));
+}
+
+static ssize_t reipl_type_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ int rc = -EINVAL;
+
+ if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0)
+ rc = reipl_set_type(IPL_TYPE_CCW);
+ else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
+ rc = reipl_set_type(IPL_TYPE_FCP);
+ else if (strncmp(buf, IPL_NSS_STR, strlen(IPL_NSS_STR)) == 0)
+ rc = reipl_set_type(IPL_TYPE_NSS);
+ return (rc != 0) ? rc : len;
+}
+
+static struct kobj_attribute reipl_type_attr =
+ __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store);
+
+static struct kset *reipl_kset;
+static struct kset *reipl_fcp_kset;
+
+static void get_ipl_string(char *dst, struct ipl_parameter_block *ipb,
+ const enum ipl_method m)
+{
+ char loadparm[LOADPARM_LEN + 1] = {};
+ char vmparm[DIAG308_VMPARM_SIZE + 1] = {};
+ char nss_name[NSS_NAME_SIZE + 1] = {};
+ size_t pos = 0;
+
+ reipl_get_ascii_loadparm(loadparm, ipb);
+ reipl_get_ascii_nss_name(nss_name, ipb);
+ reipl_get_ascii_vmparm(vmparm, sizeof(vmparm), ipb);
+
+ switch (m) {
+ case REIPL_METHOD_CCW_VM:
+ pos = sprintf(dst, "IPL %X CLEAR", ipb->ipl_info.ccw.devno);
+ break;
+ case REIPL_METHOD_NSS:
+ pos = sprintf(dst, "IPL %s", nss_name);
+ break;
+ default:
+ break;
+ }
+ if (strlen(loadparm) > 0)
+ pos += sprintf(dst + pos, " LOADPARM '%s'", loadparm);
+ if (strlen(vmparm) > 0)
+ sprintf(dst + pos, " PARM %s", vmparm);
+}
+
+static void __reipl_run(void *unused)
+{
+ struct ccw_dev_id devid;
+ static char buf[128];
+
+ switch (reipl_method) {
+ case REIPL_METHOD_CCW_CIO:
+ devid.devno = reipl_block_ccw->ipl_info.ccw.devno;
+ devid.ssid = 0;
+ reipl_ccw_dev(&devid);
+ break;
+ case REIPL_METHOD_CCW_VM:
+ get_ipl_string(buf, reipl_block_ccw, REIPL_METHOD_CCW_VM);
+ __cpcmd(buf, NULL, 0, NULL);
+ break;
+ case REIPL_METHOD_CCW_DIAG:
+ diag308(DIAG308_SET, reipl_block_ccw);
+ diag308(DIAG308_IPL, NULL);
+ break;
+ case REIPL_METHOD_FCP_RW_DIAG:
+ diag308(DIAG308_SET, reipl_block_fcp);
+ diag308(DIAG308_IPL, NULL);
+ break;
+ case REIPL_METHOD_FCP_RO_DIAG:
+ diag308(DIAG308_IPL, NULL);
+ break;
+ case REIPL_METHOD_FCP_RO_VM:
+ __cpcmd("IPL", NULL, 0, NULL);
+ break;
+ case REIPL_METHOD_NSS_DIAG:
+ diag308(DIAG308_SET, reipl_block_nss);
+ diag308(DIAG308_IPL, NULL);
+ break;
+ case REIPL_METHOD_NSS:
+ get_ipl_string(buf, reipl_block_nss, REIPL_METHOD_NSS);
+ __cpcmd(buf, NULL, 0, NULL);
+ break;
+ case REIPL_METHOD_DEFAULT:
+ if (MACHINE_IS_VM)
+ __cpcmd("IPL", NULL, 0, NULL);
+ diag308(DIAG308_IPL, NULL);
+ break;
+ case REIPL_METHOD_FCP_DUMP:
+ break;
+ }
+ disabled_wait((unsigned long) __builtin_return_address(0));
+}
+
+static void reipl_run(struct shutdown_trigger *trigger)
+{
+ smp_call_ipl_cpu(__reipl_run, NULL);
+}
+
+static void reipl_block_ccw_init(struct ipl_parameter_block *ipb)
+{
+ ipb->hdr.len = IPL_PARM_BLK_CCW_LEN;
+ ipb->hdr.version = IPL_PARM_BLOCK_VERSION;
+ ipb->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
+ ipb->hdr.pbt = DIAG308_IPL_TYPE_CCW;
+}
+
+static void reipl_block_ccw_fill_parms(struct ipl_parameter_block *ipb)
+{
+ /* LOADPARM */
+ /* check if read scp info worked and set loadparm */
+ if (sclp_ipl_info.is_valid)
+ memcpy(ipb->ipl_info.ccw.load_parm,
+ &sclp_ipl_info.loadparm, LOADPARM_LEN);
+ else
+ /* read scp info failed: set empty loadparm (EBCDIC blanks) */
+ memset(ipb->ipl_info.ccw.load_parm, 0x40, LOADPARM_LEN);
+ ipb->hdr.flags = DIAG308_FLAGS_LP_VALID;
+
+ /* VM PARM */
+ if (MACHINE_IS_VM && diag308_set_works &&
+ (ipl_block.ipl_info.ccw.vm_flags & DIAG308_VM_FLAGS_VP_VALID)) {
+
+ ipb->ipl_info.ccw.vm_flags |= DIAG308_VM_FLAGS_VP_VALID;
+ ipb->ipl_info.ccw.vm_parm_len =
+ ipl_block.ipl_info.ccw.vm_parm_len;
+ memcpy(ipb->ipl_info.ccw.vm_parm,
+ ipl_block.ipl_info.ccw.vm_parm, DIAG308_VMPARM_SIZE);
+ }
+}
+
+static int __init reipl_nss_init(void)
+{
+ int rc;
+
+ if (!MACHINE_IS_VM)
+ return 0;
+
+ reipl_block_nss = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!reipl_block_nss)
+ return -ENOMEM;
+
+ if (!diag308_set_works)
+ sys_reipl_nss_vmparm_attr.attr.mode = S_IRUGO;
+
+ rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group);
+ if (rc)
+ return rc;
+
+ reipl_block_ccw_init(reipl_block_nss);
+ if (ipl_info.type == IPL_TYPE_NSS) {
+ memset(reipl_block_nss->ipl_info.ccw.nss_name,
+ ' ', NSS_NAME_SIZE);
+ memcpy(reipl_block_nss->ipl_info.ccw.nss_name,
+ kernel_nss_name, strlen(kernel_nss_name));
+ ASCEBC(reipl_block_nss->ipl_info.ccw.nss_name, NSS_NAME_SIZE);
+ reipl_block_nss->ipl_info.ccw.vm_flags |=
+ DIAG308_VM_FLAGS_NSS_VALID;
+
+ reipl_block_ccw_fill_parms(reipl_block_nss);
+ }
+
+ reipl_capabilities |= IPL_TYPE_NSS;
+ return 0;
+}
+
+static int __init reipl_ccw_init(void)
+{
+ int rc;
+
+ reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!reipl_block_ccw)
+ return -ENOMEM;
+
+ if (MACHINE_IS_VM) {
+ if (!diag308_set_works)
+ sys_reipl_ccw_vmparm_attr.attr.mode = S_IRUGO;
+ rc = sysfs_create_group(&reipl_kset->kobj,
+ &reipl_ccw_attr_group_vm);
+ } else {
+ if(!diag308_set_works)
+ sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
+ rc = sysfs_create_group(&reipl_kset->kobj,
+ &reipl_ccw_attr_group_lpar);
+ }
+ if (rc)
+ return rc;
+
+ reipl_block_ccw_init(reipl_block_ccw);
+ if (ipl_info.type == IPL_TYPE_CCW) {
+ reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
+ reipl_block_ccw_fill_parms(reipl_block_ccw);
+ }
+
+ reipl_capabilities |= IPL_TYPE_CCW;
+ return 0;
+}
+
+static int __init reipl_fcp_init(void)
+{
+ int rc;
+
+ if (!diag308_set_works) {
+ if (ipl_info.type == IPL_TYPE_FCP) {
+ make_attrs_ro(reipl_fcp_attrs);
+ sys_reipl_fcp_scp_data_attr.attr.mode = S_IRUGO;
+ } else
+ return 0;
+ }
+
+ reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!reipl_block_fcp)
+ return -ENOMEM;
+
+ /* sysfs: create fcp kset for mixing attr group and bin attrs */
+ reipl_fcp_kset = kset_create_and_add(IPL_FCP_STR, NULL,
+ &reipl_kset->kobj);
+ if (!reipl_fcp_kset) {
+ free_page((unsigned long) reipl_block_fcp);
+ return -ENOMEM;
+ }
+
+ rc = sysfs_create_group(&reipl_fcp_kset->kobj, &reipl_fcp_attr_group);
+ if (rc) {
+ kset_unregister(reipl_fcp_kset);
+ free_page((unsigned long) reipl_block_fcp);
+ return rc;
+ }
+
+ rc = sysfs_create_bin_file(&reipl_fcp_kset->kobj,
+ &sys_reipl_fcp_scp_data_attr);
+ if (rc) {
+ sysfs_remove_group(&reipl_fcp_kset->kobj, &reipl_fcp_attr_group);
+ kset_unregister(reipl_fcp_kset);
+ free_page((unsigned long) reipl_block_fcp);
+ return rc;
+ }
+
+ if (ipl_info.type == IPL_TYPE_FCP)
+ memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE);
+ else {
+ reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
+ reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION;
+ reipl_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN;
+ reipl_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
+ reipl_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_IPL;
+ }
+ reipl_capabilities |= IPL_TYPE_FCP;
+ return 0;
+}
+
+static int __init reipl_type_init(void)
+{
+ enum ipl_type reipl_type = ipl_info.type;
+ struct ipl_parameter_block *reipl_block;
+ unsigned long size;
+
+ reipl_block = os_info_old_entry(OS_INFO_REIPL_BLOCK, &size);
+ if (!reipl_block)
+ goto out;
+ /*
+ * If we have an OS info reipl block, this will be used
+ */
+ if (reipl_block->hdr.pbt == DIAG308_IPL_TYPE_FCP) {
+ memcpy(reipl_block_fcp, reipl_block, size);
+ reipl_type = IPL_TYPE_FCP;
+ } else if (reipl_block->hdr.pbt == DIAG308_IPL_TYPE_CCW) {
+ memcpy(reipl_block_ccw, reipl_block, size);
+ reipl_type = IPL_TYPE_CCW;
+ }
+out:
+ return reipl_set_type(reipl_type);
+}
+
+static int __init reipl_init(void)
+{
+ int rc;
+
+ reipl_kset = kset_create_and_add("reipl", NULL, firmware_kobj);
+ if (!reipl_kset)
+ return -ENOMEM;
+ rc = sysfs_create_file(&reipl_kset->kobj, &reipl_type_attr.attr);
+ if (rc) {
+ kset_unregister(reipl_kset);
+ return rc;
+ }
+ rc = reipl_ccw_init();
+ if (rc)
+ return rc;
+ rc = reipl_fcp_init();
+ if (rc)
+ return rc;
+ rc = reipl_nss_init();
+ if (rc)
+ return rc;
+ return reipl_type_init();
+}
+
+static struct shutdown_action __refdata reipl_action = {
+ .name = SHUTDOWN_ACTION_REIPL_STR,
+ .fn = reipl_run,
+ .init = reipl_init,
+};
+
+/*
+ * dump shutdown action: Dump Linux on shutdown.
+ */
+
+/* FCP dump device attributes */
+
+DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n",
+ dump_block_fcp->ipl_info.fcp.wwpn);
+DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n",
+ dump_block_fcp->ipl_info.fcp.lun);
+DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
+ dump_block_fcp->ipl_info.fcp.bootprog);
+DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n",
+ dump_block_fcp->ipl_info.fcp.br_lba);
+DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
+ dump_block_fcp->ipl_info.fcp.devno);
+
+static struct attribute *dump_fcp_attrs[] = {
+ &sys_dump_fcp_device_attr.attr,
+ &sys_dump_fcp_wwpn_attr.attr,
+ &sys_dump_fcp_lun_attr.attr,
+ &sys_dump_fcp_bootprog_attr.attr,
+ &sys_dump_fcp_br_lba_attr.attr,
+ NULL,
+};
+
+static struct attribute_group dump_fcp_attr_group = {
+ .name = IPL_FCP_STR,
+ .attrs = dump_fcp_attrs,
+};
+
+/* CCW dump device attributes */
+
+DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
+ dump_block_ccw->ipl_info.ccw.devno);
+
+static struct attribute *dump_ccw_attrs[] = {
+ &sys_dump_ccw_device_attr.attr,
+ NULL,
+};
+
+static struct attribute_group dump_ccw_attr_group = {
+ .name = IPL_CCW_STR,
+ .attrs = dump_ccw_attrs,
+};
+
+/* dump type */
+
+static int dump_set_type(enum dump_type type)
+{
+ if (!(dump_capabilities & type))
+ return -EINVAL;
+ switch (type) {
+ case DUMP_TYPE_CCW:
+ if (diag308_set_works)
+ dump_method = DUMP_METHOD_CCW_DIAG;
+ else if (MACHINE_IS_VM)
+ dump_method = DUMP_METHOD_CCW_VM;
+ else
+ dump_method = DUMP_METHOD_CCW_CIO;
+ break;
+ case DUMP_TYPE_FCP:
+ dump_method = DUMP_METHOD_FCP_DIAG;
+ break;
+ default:
+ dump_method = DUMP_METHOD_NONE;
+ }
+ dump_type = type;
+ return 0;
+}
+
+static ssize_t dump_type_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", dump_type_str(dump_type));
+}
+
+static ssize_t dump_type_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ int rc = -EINVAL;
+
+ if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0)
+ rc = dump_set_type(DUMP_TYPE_NONE);
+ else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0)
+ rc = dump_set_type(DUMP_TYPE_CCW);
+ else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0)
+ rc = dump_set_type(DUMP_TYPE_FCP);
+ return (rc != 0) ? rc : len;
+}
+
+static struct kobj_attribute dump_type_attr =
+ __ATTR(dump_type, 0644, dump_type_show, dump_type_store);
+
+static struct kset *dump_kset;
+
+static void __dump_run(void *unused)
+{
+ struct ccw_dev_id devid;
+ static char buf[100];
+
+ switch (dump_method) {
+ case DUMP_METHOD_CCW_CIO:
+ devid.devno = dump_block_ccw->ipl_info.ccw.devno;
+ devid.ssid = 0;
+ reipl_ccw_dev(&devid);
+ break;
+ case DUMP_METHOD_CCW_VM:
+ sprintf(buf, "STORE STATUS");
+ __cpcmd(buf, NULL, 0, NULL);
+ sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
+ __cpcmd(buf, NULL, 0, NULL);
+ break;
+ case DUMP_METHOD_CCW_DIAG:
+ diag308(DIAG308_SET, dump_block_ccw);
+ diag308(DIAG308_DUMP, NULL);
+ break;
+ case DUMP_METHOD_FCP_DIAG:
+ diag308(DIAG308_SET, dump_block_fcp);
+ diag308(DIAG308_DUMP, NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+static void dump_run(struct shutdown_trigger *trigger)
+{
+ if (dump_method == DUMP_METHOD_NONE)
+ return;
+ smp_send_stop();
+ smp_call_ipl_cpu(__dump_run, NULL);
+}
+
+static int __init dump_ccw_init(void)
+{
+ int rc;
+
+ dump_block_ccw = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!dump_block_ccw)
+ return -ENOMEM;
+ rc = sysfs_create_group(&dump_kset->kobj, &dump_ccw_attr_group);
+ if (rc) {
+ free_page((unsigned long)dump_block_ccw);
+ return rc;
+ }
+ dump_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN;
+ dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
+ dump_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
+ dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
+ dump_capabilities |= DUMP_TYPE_CCW;
+ return 0;
+}
+
+static int __init dump_fcp_init(void)
+{
+ int rc;
+
+ if (!sclp_ipl_info.has_dump)
+ return 0; /* LDIPL DUMP is not installed */
+ if (!diag308_set_works)
+ return 0;
+ dump_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!dump_block_fcp)
+ return -ENOMEM;
+ rc = sysfs_create_group(&dump_kset->kobj, &dump_fcp_attr_group);
+ if (rc) {
+ free_page((unsigned long)dump_block_fcp);
+ return rc;
+ }
+ dump_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
+ dump_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION;
+ dump_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN;
+ dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
+ dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP;
+ dump_capabilities |= DUMP_TYPE_FCP;
+ return 0;
+}
+
+static int __init dump_init(void)
+{
+ int rc;
+
+ dump_kset = kset_create_and_add("dump", NULL, firmware_kobj);
+ if (!dump_kset)
+ return -ENOMEM;
+ rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr.attr);
+ if (rc) {
+ kset_unregister(dump_kset);
+ return rc;
+ }
+ rc = dump_ccw_init();
+ if (rc)
+ return rc;
+ rc = dump_fcp_init();
+ if (rc)
+ return rc;
+ dump_set_type(DUMP_TYPE_NONE);
+ return 0;
+}
+
+static struct shutdown_action __refdata dump_action = {
+ .name = SHUTDOWN_ACTION_DUMP_STR,
+ .fn = dump_run,
+ .init = dump_init,
+};
+
+static void dump_reipl_run(struct shutdown_trigger *trigger)
+{
+ u32 csum;
+
+ csum = csum_partial(reipl_block_actual, reipl_block_actual->hdr.len, 0);
+ copy_to_absolute_zero(&S390_lowcore.ipib_checksum, &csum, sizeof(csum));
+ copy_to_absolute_zero(&S390_lowcore.ipib, &reipl_block_actual,
+ sizeof(reipl_block_actual));
+ dump_run(trigger);
+}
+
+static int __init dump_reipl_init(void)
+{
+ if (!diag308_set_works)
+ return -EOPNOTSUPP;
+ else
+ return 0;
+}
+
+static struct shutdown_action __refdata dump_reipl_action = {
+ .name = SHUTDOWN_ACTION_DUMP_REIPL_STR,
+ .fn = dump_reipl_run,
+ .init = dump_reipl_init,
+};
+
+/*
+ * vmcmd shutdown action: Trigger vm command on shutdown.
+ */
+
+static char vmcmd_on_reboot[128];
+static char vmcmd_on_panic[128];
+static char vmcmd_on_halt[128];
+static char vmcmd_on_poff[128];
+static char vmcmd_on_restart[128];
+
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot);
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic);
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt);
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff);
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_restart, "%s\n", "%s\n", vmcmd_on_restart);
+
+static struct attribute *vmcmd_attrs[] = {
+ &sys_vmcmd_on_reboot_attr.attr,
+ &sys_vmcmd_on_panic_attr.attr,
+ &sys_vmcmd_on_halt_attr.attr,
+ &sys_vmcmd_on_poff_attr.attr,
+ &sys_vmcmd_on_restart_attr.attr,
+ NULL,
+};
+
+static struct attribute_group vmcmd_attr_group = {
+ .attrs = vmcmd_attrs,
+};
+
+static struct kset *vmcmd_kset;
+
+static void vmcmd_run(struct shutdown_trigger *trigger)
+{
+ char *cmd, *next_cmd;
+
+ if (strcmp(trigger->name, ON_REIPL_STR) == 0)
+ cmd = vmcmd_on_reboot;
+ else if (strcmp(trigger->name, ON_PANIC_STR) == 0)
+ cmd = vmcmd_on_panic;
+ else if (strcmp(trigger->name, ON_HALT_STR) == 0)
+ cmd = vmcmd_on_halt;
+ else if (strcmp(trigger->name, ON_POFF_STR) == 0)
+ cmd = vmcmd_on_poff;
+ else if (strcmp(trigger->name, ON_RESTART_STR) == 0)
+ cmd = vmcmd_on_restart;
+ else
+ return;
+
+ if (strlen(cmd) == 0)
+ return;
+ do {
+ next_cmd = strchr(cmd, '\n');
+ if (next_cmd) {
+ next_cmd[0] = 0;
+ next_cmd += 1;
+ }
+ __cpcmd(cmd, NULL, 0, NULL);
+ cmd = next_cmd;
+ } while (cmd != NULL);
+}
+
+static int vmcmd_init(void)
+{
+ if (!MACHINE_IS_VM)
+ return -EOPNOTSUPP;
+ vmcmd_kset = kset_create_and_add("vmcmd", NULL, firmware_kobj);
+ if (!vmcmd_kset)
+ return -ENOMEM;
+ return sysfs_create_group(&vmcmd_kset->kobj, &vmcmd_attr_group);
+}
+
+static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR,
+ vmcmd_run, vmcmd_init};
+
+/*
+ * stop shutdown action: Stop Linux on shutdown.
+ */
+
+static void stop_run(struct shutdown_trigger *trigger)
+{
+ if (strcmp(trigger->name, ON_PANIC_STR) == 0 ||
+ strcmp(trigger->name, ON_RESTART_STR) == 0)
+ disabled_wait((unsigned long) __builtin_return_address(0));
+ smp_stop_cpu();
+}
+
+static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR,
+ stop_run, NULL};
+
+/* action list */
+
+static struct shutdown_action *shutdown_actions_list[] = {
+ &ipl_action, &reipl_action, &dump_reipl_action, &dump_action,
+ &vmcmd_action, &stop_action};
+#define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *))
+
+/*
+ * Trigger section
+ */
+
+static struct kset *shutdown_actions_kset;
+
+static int set_trigger(const char *buf, struct shutdown_trigger *trigger,
+ size_t len)
+{
+ int i;
+
+ for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) {
+ if (sysfs_streq(buf, shutdown_actions_list[i]->name)) {
+ if (shutdown_actions_list[i]->init_rc) {
+ return shutdown_actions_list[i]->init_rc;
+ } else {
+ trigger->action = shutdown_actions_list[i];
+ return len;
+ }
+ }
+ }
+ return -EINVAL;
+}
+
+/* on reipl */
+
+static struct shutdown_trigger on_reboot_trigger = {ON_REIPL_STR,
+ &reipl_action};
+
+static ssize_t on_reboot_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", on_reboot_trigger.action->name);
+}
+
+static ssize_t on_reboot_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return set_trigger(buf, &on_reboot_trigger, len);
+}
+
+static struct kobj_attribute on_reboot_attr =
+ __ATTR(on_reboot, 0644, on_reboot_show, on_reboot_store);
+
+static void do_machine_restart(char *__unused)
+{
+ smp_send_stop();
+ on_reboot_trigger.action->fn(&on_reboot_trigger);
+ reipl_run(NULL);
+}
+void (*_machine_restart)(char *command) = do_machine_restart;
+
+/* on panic */
+
+static struct shutdown_trigger on_panic_trigger = {ON_PANIC_STR, &stop_action};
+
+static ssize_t on_panic_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", on_panic_trigger.action->name);
+}
+
+static ssize_t on_panic_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return set_trigger(buf, &on_panic_trigger, len);
+}
+
+static struct kobj_attribute on_panic_attr =
+ __ATTR(on_panic, 0644, on_panic_show, on_panic_store);
+
+static void do_panic(void)
+{
+ lgr_info_log();
+ on_panic_trigger.action->fn(&on_panic_trigger);
+ stop_run(&on_panic_trigger);
+}
+
+/* on restart */
+
+static struct shutdown_trigger on_restart_trigger = {ON_RESTART_STR,
+ &stop_action};
+
+static ssize_t on_restart_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", on_restart_trigger.action->name);
+}
+
+static ssize_t on_restart_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return set_trigger(buf, &on_restart_trigger, len);
+}
+
+static struct kobj_attribute on_restart_attr =
+ __ATTR(on_restart, 0644, on_restart_show, on_restart_store);
+
+static void __do_restart(void *ignore)
+{
+ smp_send_stop();
+#ifdef CONFIG_CRASH_DUMP
+ crash_kexec(NULL);
+#endif
+ on_restart_trigger.action->fn(&on_restart_trigger);
+ stop_run(&on_restart_trigger);
+}
+
+void do_restart(void)
+{
+ tracing_off();
+ debug_locks_off();
+ lgr_info_log();
+ smp_call_online_cpu(__do_restart, NULL);
+}
+
+/* on halt */
+
+static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action};
+
+static ssize_t on_halt_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", on_halt_trigger.action->name);
+}
+
+static ssize_t on_halt_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return set_trigger(buf, &on_halt_trigger, len);
+}
+
+static struct kobj_attribute on_halt_attr =
+ __ATTR(on_halt, 0644, on_halt_show, on_halt_store);
+
+
+static void do_machine_halt(void)
+{
+ smp_send_stop();
+ on_halt_trigger.action->fn(&on_halt_trigger);
+ stop_run(&on_halt_trigger);
+}
+void (*_machine_halt)(void) = do_machine_halt;
+
+/* on power off */
+
+static struct shutdown_trigger on_poff_trigger = {ON_POFF_STR, &stop_action};
+
+static ssize_t on_poff_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", on_poff_trigger.action->name);
+}
+
+static ssize_t on_poff_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return set_trigger(buf, &on_poff_trigger, len);
+}
+
+static struct kobj_attribute on_poff_attr =
+ __ATTR(on_poff, 0644, on_poff_show, on_poff_store);
+
+
+static void do_machine_power_off(void)
+{
+ smp_send_stop();
+ on_poff_trigger.action->fn(&on_poff_trigger);
+ stop_run(&on_poff_trigger);
+}
+void (*_machine_power_off)(void) = do_machine_power_off;
+
+static void __init shutdown_triggers_init(void)
+{
+ shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL,
+ firmware_kobj);
+ if (!shutdown_actions_kset)
+ goto fail;
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
+ &on_reboot_attr.attr))
+ goto fail;
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
+ &on_panic_attr.attr))
+ goto fail;
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
+ &on_halt_attr.attr))
+ goto fail;
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
+ &on_poff_attr.attr))
+ goto fail;
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
+ &on_restart_attr.attr))
+ goto fail;
+ return;
+fail:
+ panic("shutdown_triggers_init failed\n");
+}
+
+static void __init shutdown_actions_init(void)
+{
+ int i;
+
+ for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) {
+ if (!shutdown_actions_list[i]->init)
+ continue;
+ shutdown_actions_list[i]->init_rc =
+ shutdown_actions_list[i]->init();
+ }
+}
+
+static int __init s390_ipl_init(void)
+{
+ sclp_get_ipl_info(&sclp_ipl_info);
+ shutdown_actions_init();
+ shutdown_triggers_init();
+ return 0;
+}
+
+__initcall(s390_ipl_init);
+
+static void __init strncpy_skip_quote(char *dst, char *src, int n)
+{
+ int sx, dx;
+
+ dx = 0;
+ for (sx = 0; src[sx] != 0; sx++) {
+ if (src[sx] == '"')
+ continue;
+ dst[dx++] = src[sx];
+ if (dx >= n)
+ break;
+ }
+}
+
+static int __init vmcmd_on_reboot_setup(char *str)
+{
+ if (!MACHINE_IS_VM)
+ return 1;
+ strncpy_skip_quote(vmcmd_on_reboot, str, 127);
+ vmcmd_on_reboot[127] = 0;
+ on_reboot_trigger.action = &vmcmd_action;
+ return 1;
+}
+__setup("vmreboot=", vmcmd_on_reboot_setup);
+
+static int __init vmcmd_on_panic_setup(char *str)
+{
+ if (!MACHINE_IS_VM)
+ return 1;
+ strncpy_skip_quote(vmcmd_on_panic, str, 127);
+ vmcmd_on_panic[127] = 0;
+ on_panic_trigger.action = &vmcmd_action;
+ return 1;
+}
+__setup("vmpanic=", vmcmd_on_panic_setup);
+
+static int __init vmcmd_on_halt_setup(char *str)
+{
+ if (!MACHINE_IS_VM)
+ return 1;
+ strncpy_skip_quote(vmcmd_on_halt, str, 127);
+ vmcmd_on_halt[127] = 0;
+ on_halt_trigger.action = &vmcmd_action;
+ return 1;
+}
+__setup("vmhalt=", vmcmd_on_halt_setup);
+
+static int __init vmcmd_on_poff_setup(char *str)
+{
+ if (!MACHINE_IS_VM)
+ return 1;
+ strncpy_skip_quote(vmcmd_on_poff, str, 127);
+ vmcmd_on_poff[127] = 0;
+ on_poff_trigger.action = &vmcmd_action;
+ return 1;
+}
+__setup("vmpoff=", vmcmd_on_poff_setup);
+
+static int on_panic_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ do_panic();
+ return NOTIFY_OK;
+}
+
+static struct notifier_block on_panic_nb = {
+ .notifier_call = on_panic_notify,
+ .priority = INT_MIN,
+};
+
+void __init setup_ipl(void)
+{
+ ipl_info.type = get_ipl_type();
+ switch (ipl_info.type) {
+ case IPL_TYPE_CCW:
+ ipl_info.data.ccw.dev_id.devno = ipl_devno;
+ ipl_info.data.ccw.dev_id.ssid = 0;
+ break;
+ case IPL_TYPE_FCP:
+ case IPL_TYPE_FCP_DUMP:
+ ipl_info.data.fcp.dev_id.devno =
+ IPL_PARMBLOCK_START->ipl_info.fcp.devno;
+ ipl_info.data.fcp.dev_id.ssid = 0;
+ ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn;
+ ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun;
+ break;
+ case IPL_TYPE_NSS:
+ strncpy(ipl_info.data.nss.name, kernel_nss_name,
+ sizeof(ipl_info.data.nss.name));
+ break;
+ case IPL_TYPE_UNKNOWN:
+ /* We have no info to copy */
+ break;
+ }
+ atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
+}
+
+void __init ipl_update_parameters(void)
+{
+ int rc;
+
+ rc = diag308(DIAG308_STORE, &ipl_block);
+ if ((rc == DIAG308_RC_OK) || (rc == DIAG308_RC_NOCONFIG))
+ diag308_set_works = 1;
+}
+
+void __init ipl_save_parameters(void)
+{
+ struct cio_iplinfo iplinfo;
+ void *src, *dst;
+
+ if (cio_get_iplinfo(&iplinfo))
+ return;
+
+ ipl_devno = iplinfo.devno;
+ ipl_flags |= IPL_DEVNO_VALID;
+ if (!iplinfo.is_qdio)
+ return;
+ ipl_flags |= IPL_PARMBLOCK_VALID;
+ src = (void *)(unsigned long)S390_lowcore.ipl_parmblock_ptr;
+ dst = (void *)IPL_PARMBLOCK_ORIGIN;
+ memmove(dst, src, PAGE_SIZE);
+ S390_lowcore.ipl_parmblock_ptr = IPL_PARMBLOCK_ORIGIN;
+}
+
+static LIST_HEAD(rcall);
+static DEFINE_MUTEX(rcall_mutex);
+
+void register_reset_call(struct reset_call *reset)
+{
+ mutex_lock(&rcall_mutex);
+ list_add(&reset->list, &rcall);
+ mutex_unlock(&rcall_mutex);
+}
+EXPORT_SYMBOL_GPL(register_reset_call);
+
+void unregister_reset_call(struct reset_call *reset)
+{
+ mutex_lock(&rcall_mutex);
+ list_del(&reset->list);
+ mutex_unlock(&rcall_mutex);
+}
+EXPORT_SYMBOL_GPL(unregister_reset_call);
+
+static void do_reset_calls(void)
+{
+ struct reset_call *reset;
+
+#ifdef CONFIG_64BIT
+ if (diag308_set_works) {
+ diag308_reset();
+ return;
+ }
+#endif
+ list_for_each_entry(reset, &rcall, list)
+ reset->fn();
+}
+
+u32 dump_prefix_page;
+
+void s390_reset_system(void (*func)(void *), void *data)
+{
+ struct _lowcore *lc;
+
+ lc = (struct _lowcore *)(unsigned long) store_prefix();
+
+ /* Stack for interrupt/machine check handler */
+ lc->panic_stack = S390_lowcore.panic_stack;
+
+ /* Save prefix page address for dump case */
+ dump_prefix_page = (u32)(unsigned long) lc;
+
+ /* Disable prefixing */
+ set_prefix(0);
+
+ /* Disable lowcore protection */
+ __ctl_clear_bit(0,28);
+
+ /* Set new machine check handler */
+ S390_lowcore.mcck_new_psw.mask = psw_kernel_bits | PSW_MASK_DAT;
+ S390_lowcore.mcck_new_psw.addr =
+ PSW_ADDR_AMODE | (unsigned long) s390_base_mcck_handler;
+
+ /* Set new program check handler */
+ S390_lowcore.program_new_psw.mask = psw_kernel_bits | PSW_MASK_DAT;
+ S390_lowcore.program_new_psw.addr =
+ PSW_ADDR_AMODE | (unsigned long) s390_base_pgm_handler;
+
+ /* Store status at absolute zero */
+ store_status();
+
+ do_reset_calls();
+ if (func)
+ func(data);
+}
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
new file mode 100644
index 00000000..8a22c272
--- /dev/null
+++ b/arch/s390/kernel/irq.c
@@ -0,0 +1,283 @@
+/*
+ * Copyright IBM Corp. 2004,2011
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>,
+ * Holger Smolinski <Holger.Smolinski@de.ibm.com>,
+ * Thomas Spatzier <tspat@de.ibm.com>,
+ *
+ * This file contains interrupt related functions.
+ */
+
+#include <linux/kernel_stat.h>
+#include <linux/interrupt.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/profile.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ftrace.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <asm/irq_regs.h>
+#include <asm/cputime.h>
+#include <asm/lowcore.h>
+#include <asm/irq.h>
+#include "entry.h"
+
+struct irq_class {
+ char *name;
+ char *desc;
+};
+
+static const struct irq_class intrclass_names[] = {
+ {.name = "EXT" },
+ {.name = "I/O" },
+ {.name = "CLK", .desc = "[EXT] Clock Comparator" },
+ {.name = "EXC", .desc = "[EXT] External Call" },
+ {.name = "EMS", .desc = "[EXT] Emergency Signal" },
+ {.name = "TMR", .desc = "[EXT] CPU Timer" },
+ {.name = "TAL", .desc = "[EXT] Timing Alert" },
+ {.name = "PFL", .desc = "[EXT] Pseudo Page Fault" },
+ {.name = "DSD", .desc = "[EXT] DASD Diag" },
+ {.name = "VRT", .desc = "[EXT] Virtio" },
+ {.name = "SCP", .desc = "[EXT] Service Call" },
+ {.name = "IUC", .desc = "[EXT] IUCV" },
+ {.name = "CPM", .desc = "[EXT] CPU Measurement" },
+ {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt" },
+ {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" },
+ {.name = "DAS", .desc = "[I/O] DASD" },
+ {.name = "C15", .desc = "[I/O] 3215" },
+ {.name = "C70", .desc = "[I/O] 3270" },
+ {.name = "TAP", .desc = "[I/O] Tape" },
+ {.name = "VMR", .desc = "[I/O] Unit Record Devices" },
+ {.name = "LCS", .desc = "[I/O] LCS" },
+ {.name = "CLW", .desc = "[I/O] CLAW" },
+ {.name = "CTC", .desc = "[I/O] CTC" },
+ {.name = "APB", .desc = "[I/O] AP Bus" },
+ {.name = "CSC", .desc = "[I/O] CHSC Subchannel" },
+ {.name = "NMI", .desc = "[NMI] Machine Check" },
+};
+
+/*
+ * show_interrupts is needed by /proc/interrupts.
+ */
+int show_interrupts(struct seq_file *p, void *v)
+{
+ int i = *(loff_t *) v, j;
+
+ get_online_cpus();
+ if (i == 0) {
+ seq_puts(p, " ");
+ for_each_online_cpu(j)
+ seq_printf(p, "CPU%d ",j);
+ seq_putc(p, '\n');
+ }
+
+ if (i < NR_IRQS) {
+ seq_printf(p, "%s: ", intrclass_names[i].name);
+#ifndef CONFIG_SMP
+ seq_printf(p, "%10u ", kstat_irqs(i));
+#else
+ for_each_online_cpu(j)
+ seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
+#endif
+ if (intrclass_names[i].desc)
+ seq_printf(p, " %s", intrclass_names[i].desc);
+ seq_putc(p, '\n');
+ }
+ put_online_cpus();
+ return 0;
+}
+
+/*
+ * Switch to the asynchronous interrupt stack for softirq execution.
+ */
+asmlinkage void do_softirq(void)
+{
+ unsigned long flags, old, new;
+
+ if (in_interrupt())
+ return;
+
+ local_irq_save(flags);
+
+ if (local_softirq_pending()) {
+ /* Get current stack pointer. */
+ asm volatile("la %0,0(15)" : "=a" (old));
+ /* Check against async. stack address range. */
+ new = S390_lowcore.async_stack;
+ if (((new - old) >> (PAGE_SHIFT + THREAD_ORDER)) != 0) {
+ /* Need to switch to the async. stack. */
+ new -= STACK_FRAME_OVERHEAD;
+ ((struct stack_frame *) new)->back_chain = old;
+
+ asm volatile(" la 15,0(%0)\n"
+ " basr 14,%2\n"
+ " la 15,0(%1)\n"
+ : : "a" (new), "a" (old),
+ "a" (__do_softirq)
+ : "0", "1", "2", "3", "4", "5", "14",
+ "cc", "memory" );
+ } else {
+ /* We are already on the async stack. */
+ __do_softirq();
+ }
+ }
+
+ local_irq_restore(flags);
+}
+
+#ifdef CONFIG_PROC_FS
+void init_irq_proc(void)
+{
+ struct proc_dir_entry *root_irq_dir;
+
+ root_irq_dir = proc_mkdir("irq", NULL);
+ create_prof_cpu_mask(root_irq_dir);
+}
+#endif
+
+/*
+ * ext_int_hash[index] is the list head for all external interrupts that hash
+ * to this index.
+ */
+static struct list_head ext_int_hash[256];
+
+struct ext_int_info {
+ ext_int_handler_t handler;
+ u16 code;
+ struct list_head entry;
+ struct rcu_head rcu;
+};
+
+/* ext_int_hash_lock protects the handler lists for external interrupts */
+DEFINE_SPINLOCK(ext_int_hash_lock);
+
+static void __init init_external_interrupts(void)
+{
+ int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(ext_int_hash); idx++)
+ INIT_LIST_HEAD(&ext_int_hash[idx]);
+}
+
+static inline int ext_hash(u16 code)
+{
+ return (code + (code >> 9)) & 0xff;
+}
+
+int register_external_interrupt(u16 code, ext_int_handler_t handler)
+{
+ struct ext_int_info *p;
+ unsigned long flags;
+ int index;
+
+ p = kmalloc(sizeof(*p), GFP_ATOMIC);
+ if (!p)
+ return -ENOMEM;
+ p->code = code;
+ p->handler = handler;
+ index = ext_hash(code);
+
+ spin_lock_irqsave(&ext_int_hash_lock, flags);
+ list_add_rcu(&p->entry, &ext_int_hash[index]);
+ spin_unlock_irqrestore(&ext_int_hash_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(register_external_interrupt);
+
+int unregister_external_interrupt(u16 code, ext_int_handler_t handler)
+{
+ struct ext_int_info *p;
+ unsigned long flags;
+ int index = ext_hash(code);
+
+ spin_lock_irqsave(&ext_int_hash_lock, flags);
+ list_for_each_entry_rcu(p, &ext_int_hash[index], entry) {
+ if (p->code == code && p->handler == handler) {
+ list_del_rcu(&p->entry);
+ kfree_rcu(p, rcu);
+ }
+ }
+ spin_unlock_irqrestore(&ext_int_hash_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(unregister_external_interrupt);
+
+void __irq_entry do_extint(struct pt_regs *regs, struct ext_code ext_code,
+ unsigned int param32, unsigned long param64)
+{
+ struct pt_regs *old_regs;
+ struct ext_int_info *p;
+ int index;
+
+ old_regs = set_irq_regs(regs);
+ irq_enter();
+ if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) {
+ /* Serve timer interrupts first. */
+ clock_comparator_work();
+ }
+ kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++;
+ if (ext_code.code != 0x1004)
+ __get_cpu_var(s390_idle).nohz_delay = 1;
+
+ index = ext_hash(ext_code.code);
+ rcu_read_lock();
+ list_for_each_entry_rcu(p, &ext_int_hash[index], entry)
+ if (likely(p->code == ext_code.code))
+ p->handler(ext_code, param32, param64);
+ rcu_read_unlock();
+ irq_exit();
+ set_irq_regs(old_regs);
+}
+
+void __init init_IRQ(void)
+{
+ init_external_interrupts();
+}
+
+static DEFINE_SPINLOCK(sc_irq_lock);
+static int sc_irq_refcount;
+
+void service_subclass_irq_register(void)
+{
+ spin_lock(&sc_irq_lock);
+ if (!sc_irq_refcount)
+ ctl_set_bit(0, 9);
+ sc_irq_refcount++;
+ spin_unlock(&sc_irq_lock);
+}
+EXPORT_SYMBOL(service_subclass_irq_register);
+
+void service_subclass_irq_unregister(void)
+{
+ spin_lock(&sc_irq_lock);
+ sc_irq_refcount--;
+ if (!sc_irq_refcount)
+ ctl_clear_bit(0, 9);
+ spin_unlock(&sc_irq_lock);
+}
+EXPORT_SYMBOL(service_subclass_irq_unregister);
+
+static DEFINE_SPINLOCK(ma_subclass_lock);
+static int ma_subclass_refcount;
+
+void measurement_alert_subclass_register(void)
+{
+ spin_lock(&ma_subclass_lock);
+ if (!ma_subclass_refcount)
+ ctl_set_bit(0, 5);
+ ma_subclass_refcount++;
+ spin_unlock(&ma_subclass_lock);
+}
+EXPORT_SYMBOL(measurement_alert_subclass_register);
+
+void measurement_alert_subclass_unregister(void)
+{
+ spin_lock(&ma_subclass_lock);
+ ma_subclass_refcount--;
+ if (!ma_subclass_refcount)
+ ctl_clear_bit(0, 5);
+ spin_unlock(&ma_subclass_lock);
+}
+EXPORT_SYMBOL(measurement_alert_subclass_unregister);
diff --git a/arch/s390/kernel/jump_label.c b/arch/s390/kernel/jump_label.c
new file mode 100644
index 00000000..b987ab2c
--- /dev/null
+++ b/arch/s390/kernel/jump_label.c
@@ -0,0 +1,70 @@
+/*
+ * Jump label s390 support
+ *
+ * Copyright IBM Corp. 2011
+ * Author(s): Jan Glauber <jang@linux.vnet.ibm.com>
+ */
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/stop_machine.h>
+#include <linux/jump_label.h>
+#include <asm/ipl.h>
+
+#ifdef HAVE_JUMP_LABEL
+
+struct insn {
+ u16 opcode;
+ s32 offset;
+} __packed;
+
+struct insn_args {
+ struct jump_entry *entry;
+ enum jump_label_type type;
+};
+
+static void __jump_label_transform(struct jump_entry *entry,
+ enum jump_label_type type)
+{
+ struct insn insn;
+ int rc;
+
+ if (type == JUMP_LABEL_ENABLE) {
+ /* brcl 15,offset */
+ insn.opcode = 0xc0f4;
+ insn.offset = (entry->target - entry->code) >> 1;
+ } else {
+ /* brcl 0,0 */
+ insn.opcode = 0xc004;
+ insn.offset = 0;
+ }
+
+ rc = probe_kernel_write((void *)entry->code, &insn, JUMP_LABEL_NOP_SIZE);
+ WARN_ON_ONCE(rc < 0);
+}
+
+static int __sm_arch_jump_label_transform(void *data)
+{
+ struct insn_args *args = data;
+
+ __jump_label_transform(args->entry, args->type);
+ return 0;
+}
+
+void arch_jump_label_transform(struct jump_entry *entry,
+ enum jump_label_type type)
+{
+ struct insn_args args;
+
+ args.entry = entry;
+ args.type = type;
+
+ stop_machine(__sm_arch_jump_label_transform, &args, NULL);
+}
+
+void arch_jump_label_transform_static(struct jump_entry *entry,
+ enum jump_label_type type)
+{
+ __jump_label_transform(entry, type);
+}
+
+#endif
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
new file mode 100644
index 00000000..64b761ae
--- /dev/null
+++ b/arch/s390/kernel/kprobes.c
@@ -0,0 +1,671 @@
+/*
+ * Kernel Probes (KProbes)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2002, 2006
+ *
+ * s390 port, used ppc64 as template. Mike Grundy <grundym@us.ibm.com>
+ */
+
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+#include <linux/preempt.h>
+#include <linux/stop_machine.h>
+#include <linux/kdebug.h>
+#include <linux/uaccess.h>
+#include <asm/cacheflush.h>
+#include <asm/sections.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/hardirq.h>
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe);
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+struct kretprobe_blackpoint kretprobe_blacklist[] = { };
+
+static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn)
+{
+ switch (insn[0] >> 8) {
+ case 0x0c: /* bassm */
+ case 0x0b: /* bsm */
+ case 0x83: /* diag */
+ case 0x44: /* ex */
+ case 0xac: /* stnsm */
+ case 0xad: /* stosm */
+ return -EINVAL;
+ }
+ switch (insn[0]) {
+ case 0x0101: /* pr */
+ case 0xb25a: /* bsa */
+ case 0xb240: /* bakr */
+ case 0xb258: /* bsg */
+ case 0xb218: /* pc */
+ case 0xb228: /* pt */
+ case 0xb98d: /* epsw */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int __kprobes get_fixup_type(kprobe_opcode_t *insn)
+{
+ /* default fixup method */
+ int fixup = FIXUP_PSW_NORMAL;
+
+ switch (insn[0] >> 8) {
+ case 0x05: /* balr */
+ case 0x0d: /* basr */
+ fixup = FIXUP_RETURN_REGISTER;
+ /* if r2 = 0, no branch will be taken */
+ if ((insn[0] & 0x0f) == 0)
+ fixup |= FIXUP_BRANCH_NOT_TAKEN;
+ break;
+ case 0x06: /* bctr */
+ case 0x07: /* bcr */
+ fixup = FIXUP_BRANCH_NOT_TAKEN;
+ break;
+ case 0x45: /* bal */
+ case 0x4d: /* bas */
+ fixup = FIXUP_RETURN_REGISTER;
+ break;
+ case 0x47: /* bc */
+ case 0x46: /* bct */
+ case 0x86: /* bxh */
+ case 0x87: /* bxle */
+ fixup = FIXUP_BRANCH_NOT_TAKEN;
+ break;
+ case 0x82: /* lpsw */
+ fixup = FIXUP_NOT_REQUIRED;
+ break;
+ case 0xb2: /* lpswe */
+ if ((insn[0] & 0xff) == 0xb2)
+ fixup = FIXUP_NOT_REQUIRED;
+ break;
+ case 0xa7: /* bras */
+ if ((insn[0] & 0x0f) == 0x05)
+ fixup |= FIXUP_RETURN_REGISTER;
+ break;
+ case 0xc0:
+ if ((insn[0] & 0x0f) == 0x00 || /* larl */
+ (insn[0] & 0x0f) == 0x05) /* brasl */
+ fixup |= FIXUP_RETURN_REGISTER;
+ break;
+ case 0xeb:
+ if ((insn[2] & 0xff) == 0x44 || /* bxhg */
+ (insn[2] & 0xff) == 0x45) /* bxleg */
+ fixup = FIXUP_BRANCH_NOT_TAKEN;
+ break;
+ case 0xe3: /* bctg */
+ if ((insn[2] & 0xff) == 0x46)
+ fixup = FIXUP_BRANCH_NOT_TAKEN;
+ break;
+ }
+ return fixup;
+}
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+ if ((unsigned long) p->addr & 0x01)
+ return -EINVAL;
+
+ /* Make sure the probe isn't going on a difficult instruction */
+ if (is_prohibited_opcode(p->addr))
+ return -EINVAL;
+
+ p->opcode = *p->addr;
+ memcpy(p->ainsn.insn, p->addr, ((p->opcode >> 14) + 3) & -2);
+
+ return 0;
+}
+
+struct ins_replace_args {
+ kprobe_opcode_t *ptr;
+ kprobe_opcode_t opcode;
+};
+
+static int __kprobes swap_instruction(void *aref)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ unsigned long status = kcb->kprobe_status;
+ struct ins_replace_args *args = aref;
+
+ kcb->kprobe_status = KPROBE_SWAP_INST;
+ probe_kernel_write(args->ptr, &args->opcode, sizeof(args->opcode));
+ kcb->kprobe_status = status;
+ return 0;
+}
+
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+ struct ins_replace_args args;
+
+ args.ptr = p->addr;
+ args.opcode = BREAKPOINT_INSTRUCTION;
+ stop_machine(swap_instruction, &args, NULL);
+}
+
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+ struct ins_replace_args args;
+
+ args.ptr = p->addr;
+ args.opcode = p->opcode;
+ stop_machine(swap_instruction, &args, NULL);
+}
+
+void __kprobes arch_remove_kprobe(struct kprobe *p)
+{
+}
+
+static void __kprobes enable_singlestep(struct kprobe_ctlblk *kcb,
+ struct pt_regs *regs,
+ unsigned long ip)
+{
+ struct per_regs per_kprobe;
+
+ /* Set up the PER control registers %cr9-%cr11 */
+ per_kprobe.control = PER_EVENT_IFETCH;
+ per_kprobe.start = ip;
+ per_kprobe.end = ip;
+
+ /* Save control regs and psw mask */
+ __ctl_store(kcb->kprobe_saved_ctl, 9, 11);
+ kcb->kprobe_saved_imask = regs->psw.mask &
+ (PSW_MASK_PER | PSW_MASK_IO | PSW_MASK_EXT);
+
+ /* Set PER control regs, turns on single step for the given address */
+ __ctl_load(per_kprobe, 9, 11);
+ regs->psw.mask |= PSW_MASK_PER;
+ regs->psw.mask &= ~(PSW_MASK_IO | PSW_MASK_EXT);
+ regs->psw.addr = ip | PSW_ADDR_AMODE;
+}
+
+static void __kprobes disable_singlestep(struct kprobe_ctlblk *kcb,
+ struct pt_regs *regs,
+ unsigned long ip)
+{
+ /* Restore control regs and psw mask, set new psw address */
+ __ctl_load(kcb->kprobe_saved_ctl, 9, 11);
+ regs->psw.mask &= ~PSW_MASK_PER;
+ regs->psw.mask |= kcb->kprobe_saved_imask;
+ regs->psw.addr = ip | PSW_ADDR_AMODE;
+}
+
+/*
+ * Activate a kprobe by storing its pointer to current_kprobe. The
+ * previous kprobe is stored in kcb->prev_kprobe. A stack of up to
+ * two kprobes can be active, see KPROBE_REENTER.
+ */
+static void __kprobes push_kprobe(struct kprobe_ctlblk *kcb, struct kprobe *p)
+{
+ kcb->prev_kprobe.kp = __get_cpu_var(current_kprobe);
+ kcb->prev_kprobe.status = kcb->kprobe_status;
+ __get_cpu_var(current_kprobe) = p;
+}
+
+/*
+ * Deactivate a kprobe by backing up to the previous state. If the
+ * current state is KPROBE_REENTER prev_kprobe.kp will be non-NULL,
+ * for any other state prev_kprobe.kp will be NULL.
+ */
+static void __kprobes pop_kprobe(struct kprobe_ctlblk *kcb)
+{
+ __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
+ kcb->kprobe_status = kcb->prev_kprobe.status;
+}
+
+void __kprobes arch_prepare_kretprobe(struct kretprobe_instance *ri,
+ struct pt_regs *regs)
+{
+ ri->ret_addr = (kprobe_opcode_t *) regs->gprs[14];
+
+ /* Replace the return addr with trampoline addr */
+ regs->gprs[14] = (unsigned long) &kretprobe_trampoline;
+}
+
+static void __kprobes kprobe_reenter_check(struct kprobe_ctlblk *kcb,
+ struct kprobe *p)
+{
+ switch (kcb->kprobe_status) {
+ case KPROBE_HIT_SSDONE:
+ case KPROBE_HIT_ACTIVE:
+ kprobes_inc_nmissed_count(p);
+ break;
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ default:
+ /*
+ * A kprobe on the code path to single step an instruction
+ * is a BUG. The code path resides in the .kprobes.text
+ * section and is executed with interrupts disabled.
+ */
+ printk(KERN_EMERG "Invalid kprobe detected at %p.\n", p->addr);
+ dump_kprobe(p);
+ BUG();
+ }
+}
+
+static int __kprobes kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb;
+ struct kprobe *p;
+
+ /*
+ * We want to disable preemption for the entire duration of kprobe
+ * processing. That includes the calls to the pre/post handlers
+ * and single stepping the kprobe instruction.
+ */
+ preempt_disable();
+ kcb = get_kprobe_ctlblk();
+ p = get_kprobe((void *)((regs->psw.addr & PSW_ADDR_INSN) - 2));
+
+ if (p) {
+ if (kprobe_running()) {
+ /*
+ * We have hit a kprobe while another is still
+ * active. This can happen in the pre and post
+ * handler. Single step the instruction of the
+ * new probe but do not call any handler function
+ * of this secondary kprobe.
+ * push_kprobe and pop_kprobe saves and restores
+ * the currently active kprobe.
+ */
+ kprobe_reenter_check(kcb, p);
+ push_kprobe(kcb, p);
+ kcb->kprobe_status = KPROBE_REENTER;
+ } else {
+ /*
+ * If we have no pre-handler or it returned 0, we
+ * continue with single stepping. If we have a
+ * pre-handler and it returned non-zero, it prepped
+ * for calling the break_handler below on re-entry
+ * for jprobe processing, so get out doing nothing
+ * more here.
+ */
+ push_kprobe(kcb, p);
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+ if (p->pre_handler && p->pre_handler(p, regs))
+ return 1;
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ }
+ enable_singlestep(kcb, regs, (unsigned long) p->ainsn.insn);
+ return 1;
+ } else if (kprobe_running()) {
+ p = __get_cpu_var(current_kprobe);
+ if (p->break_handler && p->break_handler(p, regs)) {
+ /*
+ * Continuation after the jprobe completed and
+ * caused the jprobe_return trap. The jprobe
+ * break_handler "returns" to the original
+ * function that still has the kprobe breakpoint
+ * installed. We continue with single stepping.
+ */
+ kcb->kprobe_status = KPROBE_HIT_SS;
+ enable_singlestep(kcb, regs,
+ (unsigned long) p->ainsn.insn);
+ return 1;
+ } /* else:
+ * No kprobe at this address and the current kprobe
+ * has no break handler (no jprobe!). The kernel just
+ * exploded, let the standard trap handler pick up the
+ * pieces.
+ */
+ } /* else:
+ * No kprobe at this address and no active kprobe. The trap has
+ * not been caused by a kprobe breakpoint. The race of breakpoint
+ * vs. kprobe remove does not exist because on s390 as we use
+ * stop_machine to arm/disarm the breakpoints.
+ */
+ preempt_enable_no_resched();
+ return 0;
+}
+
+/*
+ * Function return probe trampoline:
+ * - init_kprobes() establishes a probepoint here
+ * - When the probed function returns, this probe
+ * causes the handlers to fire
+ */
+static void __used kretprobe_trampoline_holder(void)
+{
+ asm volatile(".global kretprobe_trampoline\n"
+ "kretprobe_trampoline: bcr 0,0\n");
+}
+
+/*
+ * Called when the probe at kretprobe trampoline is hit
+ */
+static int __kprobes trampoline_probe_handler(struct kprobe *p,
+ struct pt_regs *regs)
+{
+ struct kretprobe_instance *ri;
+ struct hlist_head *head, empty_rp;
+ struct hlist_node *node, *tmp;
+ unsigned long flags, orig_ret_address;
+ unsigned long trampoline_address;
+ kprobe_opcode_t *correct_ret_addr;
+
+ INIT_HLIST_HEAD(&empty_rp);
+ kretprobe_hash_lock(current, &head, &flags);
+
+ /*
+ * It is possible to have multiple instances associated with a given
+ * task either because an multiple functions in the call path
+ * have a return probe installed on them, and/or more than one return
+ * return probe was registered for a target function.
+ *
+ * We can handle this because:
+ * - instances are always inserted at the head of the list
+ * - when multiple return probes are registered for the same
+ * function, the first instance's ret_addr will point to the
+ * real return address, and all the rest will point to
+ * kretprobe_trampoline
+ */
+ ri = NULL;
+ orig_ret_address = 0;
+ correct_ret_addr = NULL;
+ trampoline_address = (unsigned long) &kretprobe_trampoline;
+ hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+ if (ri->task != current)
+ /* another task is sharing our hash bucket */
+ continue;
+
+ orig_ret_address = (unsigned long) ri->ret_addr;
+
+ if (orig_ret_address != trampoline_address)
+ /*
+ * This is the real return address. Any other
+ * instances associated with this task are for
+ * other calls deeper on the call stack
+ */
+ break;
+ }
+
+ kretprobe_assert(ri, orig_ret_address, trampoline_address);
+
+ correct_ret_addr = ri->ret_addr;
+ hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+ if (ri->task != current)
+ /* another task is sharing our hash bucket */
+ continue;
+
+ orig_ret_address = (unsigned long) ri->ret_addr;
+
+ if (ri->rp && ri->rp->handler) {
+ ri->ret_addr = correct_ret_addr;
+ ri->rp->handler(ri, regs);
+ }
+
+ recycle_rp_inst(ri, &empty_rp);
+
+ if (orig_ret_address != trampoline_address)
+ /*
+ * This is the real return address. Any other
+ * instances associated with this task are for
+ * other calls deeper on the call stack
+ */
+ break;
+ }
+
+ regs->psw.addr = orig_ret_address | PSW_ADDR_AMODE;
+
+ pop_kprobe(get_kprobe_ctlblk());
+ kretprobe_hash_unlock(current, &flags);
+ preempt_enable_no_resched();
+
+ hlist_for_each_entry_safe(ri, node, tmp, &empty_rp, hlist) {
+ hlist_del(&ri->hlist);
+ kfree(ri);
+ }
+ /*
+ * By returning a non-zero value, we are telling
+ * kprobe_handler() that we don't want the post_handler
+ * to run (and have re-enabled preemption)
+ */
+ return 1;
+}
+
+/*
+ * Called after single-stepping. p->addr is the address of the
+ * instruction whose first byte has been replaced by the "breakpoint"
+ * instruction. To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction. The address of this
+ * copy is p->ainsn.insn.
+ */
+static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ unsigned long ip = regs->psw.addr & PSW_ADDR_INSN;
+ int fixup = get_fixup_type(p->ainsn.insn);
+
+ if (fixup & FIXUP_PSW_NORMAL)
+ ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn;
+
+ if (fixup & FIXUP_BRANCH_NOT_TAKEN) {
+ int ilen = ((p->ainsn.insn[0] >> 14) + 3) & -2;
+ if (ip - (unsigned long) p->ainsn.insn == ilen)
+ ip = (unsigned long) p->addr + ilen;
+ }
+
+ if (fixup & FIXUP_RETURN_REGISTER) {
+ int reg = (p->ainsn.insn[0] & 0xf0) >> 4;
+ regs->gprs[reg] += (unsigned long) p->addr -
+ (unsigned long) p->ainsn.insn;
+ }
+
+ disable_singlestep(kcb, regs, ip);
+}
+
+static int __kprobes post_kprobe_handler(struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ struct kprobe *p = kprobe_running();
+
+ if (!p)
+ return 0;
+
+ if (kcb->kprobe_status != KPROBE_REENTER && p->post_handler) {
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+ p->post_handler(p, regs, 0);
+ }
+
+ resume_execution(p, regs);
+ pop_kprobe(kcb);
+ preempt_enable_no_resched();
+
+ /*
+ * if somebody else is singlestepping across a probe point, psw mask
+ * will have PER set, in which case, continue the remaining processing
+ * of do_single_step, as if this is not a probe hit.
+ */
+ if (regs->psw.mask & PSW_MASK_PER)
+ return 0;
+
+ return 1;
+}
+
+static int __kprobes kprobe_trap_handler(struct pt_regs *regs, int trapnr)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ struct kprobe *p = kprobe_running();
+ const struct exception_table_entry *entry;
+
+ switch(kcb->kprobe_status) {
+ case KPROBE_SWAP_INST:
+ /* We are here because the instruction replacement failed */
+ return 0;
+ case KPROBE_HIT_SS:
+ case KPROBE_REENTER:
+ /*
+ * We are here because the instruction being single
+ * stepped caused a page fault. We reset the current
+ * kprobe and the nip points back to the probe address
+ * and allow the page fault handler to continue as a
+ * normal page fault.
+ */
+ disable_singlestep(kcb, regs, (unsigned long) p->addr);
+ pop_kprobe(kcb);
+ preempt_enable_no_resched();
+ break;
+ case KPROBE_HIT_ACTIVE:
+ case KPROBE_HIT_SSDONE:
+ /*
+ * We increment the nmissed count for accounting,
+ * we can also use npre/npostfault count for accouting
+ * these specific fault cases.
+ */
+ kprobes_inc_nmissed_count(p);
+
+ /*
+ * We come here because instructions in the pre/post
+ * handler caused the page_fault, this could happen
+ * if handler tries to access user space by
+ * copy_from_user(), get_user() etc. Let the
+ * user-specified handler try to fix it first.
+ */
+ if (p->fault_handler && p->fault_handler(p, regs, trapnr))
+ return 1;
+
+ /*
+ * In case the user-specified fault handler returned
+ * zero, try to fix up.
+ */
+ entry = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
+ if (entry) {
+ regs->psw.addr = entry->fixup | PSW_ADDR_AMODE;
+ return 1;
+ }
+
+ /*
+ * fixup_exception() could not handle it,
+ * Let do_page_fault() fix it.
+ */
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+ int ret;
+
+ if (regs->psw.mask & (PSW_MASK_IO | PSW_MASK_EXT))
+ local_irq_disable();
+ ret = kprobe_trap_handler(regs, trapnr);
+ if (regs->psw.mask & (PSW_MASK_IO | PSW_MASK_EXT))
+ local_irq_restore(regs->psw.mask & ~PSW_MASK_PER);
+ return ret;
+}
+
+/*
+ * Wrapper routine to for handling exceptions.
+ */
+int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
+ unsigned long val, void *data)
+{
+ struct die_args *args = (struct die_args *) data;
+ struct pt_regs *regs = args->regs;
+ int ret = NOTIFY_DONE;
+
+ if (regs->psw.mask & (PSW_MASK_IO | PSW_MASK_EXT))
+ local_irq_disable();
+
+ switch (val) {
+ case DIE_BPT:
+ if (kprobe_handler(regs))
+ ret = NOTIFY_STOP;
+ break;
+ case DIE_SSTEP:
+ if (post_kprobe_handler(regs))
+ ret = NOTIFY_STOP;
+ break;
+ case DIE_TRAP:
+ if (!preemptible() && kprobe_running() &&
+ kprobe_trap_handler(regs, args->trapnr))
+ ret = NOTIFY_STOP;
+ break;
+ default:
+ break;
+ }
+
+ if (regs->psw.mask & (PSW_MASK_IO | PSW_MASK_EXT))
+ local_irq_restore(regs->psw.mask & ~PSW_MASK_PER);
+
+ return ret;
+}
+
+int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct jprobe *jp = container_of(p, struct jprobe, kp);
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ unsigned long stack;
+
+ memcpy(&kcb->jprobe_saved_regs, regs, sizeof(struct pt_regs));
+
+ /* setup return addr to the jprobe handler routine */
+ regs->psw.addr = (unsigned long) jp->entry | PSW_ADDR_AMODE;
+ regs->psw.mask &= ~(PSW_MASK_IO | PSW_MASK_EXT);
+
+ /* r15 is the stack pointer */
+ stack = (unsigned long) regs->gprs[15];
+
+ memcpy(kcb->jprobes_stack, (void *) stack, MIN_STACK_SIZE(stack));
+ return 1;
+}
+
+void __kprobes jprobe_return(void)
+{
+ asm volatile(".word 0x0002");
+}
+
+static void __used __kprobes jprobe_return_end(void)
+{
+ asm volatile("bcr 0,0");
+}
+
+int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+ struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+ unsigned long stack;
+
+ stack = (unsigned long) kcb->jprobe_saved_regs.gprs[15];
+
+ /* Put the regs back */
+ memcpy(regs, &kcb->jprobe_saved_regs, sizeof(struct pt_regs));
+ /* put the stack back */
+ memcpy((void *) stack, kcb->jprobes_stack, MIN_STACK_SIZE(stack));
+ preempt_enable_no_resched();
+ return 1;
+}
+
+static struct kprobe trampoline = {
+ .addr = (kprobe_opcode_t *) &kretprobe_trampoline,
+ .pre_handler = trampoline_probe_handler
+};
+
+int __init arch_init_kprobes(void)
+{
+ return register_kprobe(&trampoline);
+}
+
+int __kprobes arch_trampoline_kprobe(struct kprobe *p)
+{
+ return p->addr == (kprobe_opcode_t *) &kretprobe_trampoline;
+}
diff --git a/arch/s390/kernel/lgr.c b/arch/s390/kernel/lgr.c
new file mode 100644
index 00000000..87f080b1
--- /dev/null
+++ b/arch/s390/kernel/lgr.c
@@ -0,0 +1,200 @@
+/*
+ * Linux Guest Relocation (LGR) detection
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <asm/facility.h>
+#include <asm/sysinfo.h>
+#include <asm/ebcdic.h>
+#include <asm/debug.h>
+#include <asm/ipl.h>
+
+#define LGR_TIMER_INTERVAL_SECS (30 * 60)
+#define VM_LEVEL_MAX 2 /* Maximum is 8, but we only record two levels */
+
+/*
+ * LGR info: Contains stfle and stsi data
+ */
+struct lgr_info {
+ /* Bit field with facility information: 4 DWORDs are stored */
+ u64 stfle_fac_list[4];
+ /* Level of system (1 = CEC, 2 = LPAR, 3 = z/VM */
+ u32 level;
+ /* Level 1: CEC info (stsi 1.1.1) */
+ char manufacturer[16];
+ char type[4];
+ char sequence[16];
+ char plant[4];
+ char model[16];
+ /* Level 2: LPAR info (stsi 2.2.2) */
+ u16 lpar_number;
+ char name[8];
+ /* Level 3: VM info (stsi 3.2.2) */
+ u8 vm_count;
+ struct {
+ char name[8];
+ char cpi[16];
+ } vm[VM_LEVEL_MAX];
+} __packed __aligned(8);
+
+/*
+ * LGR globals
+ */
+static void *lgr_page;
+static struct lgr_info lgr_info_last;
+static struct lgr_info lgr_info_cur;
+static struct debug_info *lgr_dbf;
+
+/*
+ * Return number of valid stsi levels
+ */
+static inline int stsi_0(void)
+{
+ int rc = stsi(NULL, 0, 0, 0);
+
+ return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
+}
+
+/*
+ * Copy buffer and then convert it to ASCII
+ */
+static void cpascii(char *dst, char *src, int size)
+{
+ memcpy(dst, src, size);
+ EBCASC(dst, size);
+}
+
+/*
+ * Fill LGR info with 1.1.1 stsi data
+ */
+static void lgr_stsi_1_1_1(struct lgr_info *lgr_info)
+{
+ struct sysinfo_1_1_1 *si = lgr_page;
+
+ if (stsi(si, 1, 1, 1) == -ENOSYS)
+ return;
+ cpascii(lgr_info->manufacturer, si->manufacturer,
+ sizeof(si->manufacturer));
+ cpascii(lgr_info->type, si->type, sizeof(si->type));
+ cpascii(lgr_info->model, si->model, sizeof(si->model));
+ cpascii(lgr_info->sequence, si->sequence, sizeof(si->sequence));
+ cpascii(lgr_info->plant, si->plant, sizeof(si->plant));
+}
+
+/*
+ * Fill LGR info with 2.2.2 stsi data
+ */
+static void lgr_stsi_2_2_2(struct lgr_info *lgr_info)
+{
+ struct sysinfo_2_2_2 *si = lgr_page;
+
+ if (stsi(si, 2, 2, 2) == -ENOSYS)
+ return;
+ cpascii(lgr_info->name, si->name, sizeof(si->name));
+ memcpy(&lgr_info->lpar_number, &si->lpar_number,
+ sizeof(lgr_info->lpar_number));
+}
+
+/*
+ * Fill LGR info with 3.2.2 stsi data
+ */
+static void lgr_stsi_3_2_2(struct lgr_info *lgr_info)
+{
+ struct sysinfo_3_2_2 *si = lgr_page;
+ int i;
+
+ if (stsi(si, 3, 2, 2) == -ENOSYS)
+ return;
+ for (i = 0; i < min_t(u8, si->count, VM_LEVEL_MAX); i++) {
+ cpascii(lgr_info->vm[i].name, si->vm[i].name,
+ sizeof(si->vm[i].name));
+ cpascii(lgr_info->vm[i].cpi, si->vm[i].cpi,
+ sizeof(si->vm[i].cpi));
+ }
+ lgr_info->vm_count = si->count;
+}
+
+/*
+ * Fill LGR info with current data
+ */
+static void lgr_info_get(struct lgr_info *lgr_info)
+{
+ memset(lgr_info, 0, sizeof(*lgr_info));
+ stfle(lgr_info->stfle_fac_list, ARRAY_SIZE(lgr_info->stfle_fac_list));
+ lgr_info->level = stsi_0();
+ if (lgr_info->level == -ENOSYS)
+ return;
+ if (lgr_info->level >= 1)
+ lgr_stsi_1_1_1(lgr_info);
+ if (lgr_info->level >= 2)
+ lgr_stsi_2_2_2(lgr_info);
+ if (lgr_info->level >= 3)
+ lgr_stsi_3_2_2(lgr_info);
+}
+
+/*
+ * Check if LGR info has changed and if yes log new LGR info to s390dbf
+ */
+void lgr_info_log(void)
+{
+ static DEFINE_SPINLOCK(lgr_info_lock);
+ unsigned long flags;
+
+ if (!spin_trylock_irqsave(&lgr_info_lock, flags))
+ return;
+ lgr_info_get(&lgr_info_cur);
+ if (memcmp(&lgr_info_last, &lgr_info_cur, sizeof(lgr_info_cur)) != 0) {
+ debug_event(lgr_dbf, 1, &lgr_info_cur, sizeof(lgr_info_cur));
+ lgr_info_last = lgr_info_cur;
+ }
+ spin_unlock_irqrestore(&lgr_info_lock, flags);
+}
+EXPORT_SYMBOL_GPL(lgr_info_log);
+
+static void lgr_timer_set(void);
+
+/*
+ * LGR timer callback
+ */
+static void lgr_timer_fn(unsigned long ignored)
+{
+ lgr_info_log();
+ lgr_timer_set();
+}
+
+static struct timer_list lgr_timer =
+ TIMER_DEFERRED_INITIALIZER(lgr_timer_fn, 0, 0);
+
+/*
+ * Setup next LGR timer
+ */
+static void lgr_timer_set(void)
+{
+ mod_timer(&lgr_timer, jiffies + LGR_TIMER_INTERVAL_SECS * HZ);
+}
+
+/*
+ * Initialize LGR: Add s390dbf, write initial lgr_info and setup timer
+ */
+static int __init lgr_init(void)
+{
+ lgr_page = (void *) __get_free_pages(GFP_KERNEL, 0);
+ if (!lgr_page)
+ return -ENOMEM;
+ lgr_dbf = debug_register("lgr", 1, 1, sizeof(struct lgr_info));
+ if (!lgr_dbf) {
+ free_page((unsigned long) lgr_page);
+ return -ENOMEM;
+ }
+ debug_register_view(lgr_dbf, &debug_hex_ascii_view);
+ lgr_info_get(&lgr_info_last);
+ debug_event(lgr_dbf, 1, &lgr_info_last, sizeof(lgr_info_last));
+ lgr_timer_set();
+ return 0;
+}
+module_init(lgr_init);
diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c
new file mode 100644
index 00000000..bdad47d5
--- /dev/null
+++ b/arch/s390/kernel/machine_kexec.c
@@ -0,0 +1,234 @@
+/*
+ * arch/s390/kernel/machine_kexec.c
+ *
+ * Copyright IBM Corp. 2005,2011
+ *
+ * Author(s): Rolf Adelsberger,
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ * Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#include <linux/device.h>
+#include <linux/mm.h>
+#include <linux/kexec.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/ftrace.h>
+#include <linux/debug_locks.h>
+#include <asm/cio.h>
+#include <asm/setup.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/smp.h>
+#include <asm/reset.h>
+#include <asm/ipl.h>
+#include <asm/diag.h>
+#include <asm/asm-offsets.h>
+
+typedef void (*relocate_kernel_t)(kimage_entry_t *, unsigned long);
+
+extern const unsigned char relocate_kernel[];
+extern const unsigned long long relocate_kernel_len;
+
+#ifdef CONFIG_CRASH_DUMP
+
+void *fill_cpu_elf_notes(void *ptr, struct save_area *sa);
+
+/*
+ * Create ELF notes for one CPU
+ */
+static void add_elf_notes(int cpu)
+{
+ struct save_area *sa = (void *) 4608 + store_prefix();
+ void *ptr;
+
+ memcpy((void *) (4608UL + sa->pref_reg), sa, sizeof(*sa));
+ ptr = (u64 *) per_cpu_ptr(crash_notes, cpu);
+ ptr = fill_cpu_elf_notes(ptr, sa);
+ memset(ptr, 0, sizeof(struct elf_note));
+}
+
+/*
+ * Initialize CPU ELF notes
+ */
+void setup_regs(void)
+{
+ unsigned long sa = S390_lowcore.prefixreg_save_area + SAVE_AREA_BASE;
+ int cpu, this_cpu;
+
+ this_cpu = smp_find_processor_id(stap());
+ add_elf_notes(this_cpu);
+ for_each_online_cpu(cpu) {
+ if (cpu == this_cpu)
+ continue;
+ if (smp_store_status(cpu))
+ continue;
+ add_elf_notes(cpu);
+ }
+ /* Copy dump CPU store status info to absolute zero */
+ memcpy((void *) SAVE_AREA_BASE, (void *) sa, sizeof(struct save_area));
+}
+
+#endif
+
+/*
+ * Start kdump: We expect here that a store status has been done on our CPU
+ */
+static void __do_machine_kdump(void *image)
+{
+#ifdef CONFIG_CRASH_DUMP
+ int (*start_kdump)(int) = (void *)((struct kimage *) image)->start;
+
+ __load_psw_mask(PSW_MASK_BASE | PSW_DEFAULT_KEY | PSW_MASK_EA | PSW_MASK_BA);
+ setup_regs();
+ start_kdump(1);
+#endif
+}
+
+/*
+ * Check if kdump checksums are valid: We call purgatory with parameter "0"
+ */
+static int kdump_csum_valid(struct kimage *image)
+{
+#ifdef CONFIG_CRASH_DUMP
+ int (*start_kdump)(int) = (void *)image->start;
+ int rc;
+
+ __arch_local_irq_stnsm(0xfb); /* disable DAT */
+ rc = start_kdump(0);
+ __arch_local_irq_stosm(0x04); /* enable DAT */
+ return rc ? 0 : -EINVAL;
+#else
+ return -EINVAL;
+#endif
+}
+
+/*
+ * Map or unmap crashkernel memory
+ */
+static void crash_map_pages(int enable)
+{
+ unsigned long size = resource_size(&crashk_res);
+
+ BUG_ON(crashk_res.start % KEXEC_CRASH_MEM_ALIGN ||
+ size % KEXEC_CRASH_MEM_ALIGN);
+ if (enable)
+ vmem_add_mapping(crashk_res.start, size);
+ else
+ vmem_remove_mapping(crashk_res.start, size);
+}
+
+/*
+ * Map crashkernel memory
+ */
+void crash_map_reserved_pages(void)
+{
+ crash_map_pages(1);
+}
+
+/*
+ * Unmap crashkernel memory
+ */
+void crash_unmap_reserved_pages(void)
+{
+ crash_map_pages(0);
+}
+
+/*
+ * Give back memory to hypervisor before new kdump is loaded
+ */
+static int machine_kexec_prepare_kdump(void)
+{
+#ifdef CONFIG_CRASH_DUMP
+ if (MACHINE_IS_VM)
+ diag10_range(PFN_DOWN(crashk_res.start),
+ PFN_DOWN(crashk_res.end - crashk_res.start + 1));
+ return 0;
+#else
+ return -EINVAL;
+#endif
+}
+
+int machine_kexec_prepare(struct kimage *image)
+{
+ void *reboot_code_buffer;
+
+ /* Can't replace kernel image since it is read-only. */
+ if (ipl_flags & IPL_NSS_VALID)
+ return -ENOSYS;
+
+ if (image->type == KEXEC_TYPE_CRASH)
+ return machine_kexec_prepare_kdump();
+
+ /* We don't support anything but the default image type for now. */
+ if (image->type != KEXEC_TYPE_DEFAULT)
+ return -EINVAL;
+
+ /* Get the destination where the assembler code should be copied to.*/
+ reboot_code_buffer = (void *) page_to_phys(image->control_code_page);
+
+ /* Then copy it */
+ memcpy(reboot_code_buffer, relocate_kernel, relocate_kernel_len);
+ return 0;
+}
+
+void machine_kexec_cleanup(struct kimage *image)
+{
+}
+
+void arch_crash_save_vmcoreinfo(void)
+{
+ VMCOREINFO_SYMBOL(lowcore_ptr);
+ VMCOREINFO_SYMBOL(high_memory);
+ VMCOREINFO_LENGTH(lowcore_ptr, NR_CPUS);
+}
+
+void machine_shutdown(void)
+{
+}
+
+/*
+ * Do normal kexec
+ */
+static void __do_machine_kexec(void *data)
+{
+ relocate_kernel_t data_mover;
+ struct kimage *image = data;
+
+ data_mover = (relocate_kernel_t) page_to_phys(image->control_code_page);
+
+ /* Call the moving routine */
+ (*data_mover)(&image->head, image->start);
+}
+
+/*
+ * Reset system and call either kdump or normal kexec
+ */
+static void __machine_kexec(void *data)
+{
+ struct kimage *image = data;
+
+ pfault_fini();
+ tracing_off();
+ debug_locks_off();
+ if (image->type == KEXEC_TYPE_CRASH) {
+ lgr_info_log();
+ s390_reset_system(__do_machine_kdump, data);
+ } else {
+ s390_reset_system(__do_machine_kexec, data);
+ }
+ disabled_wait((unsigned long) __builtin_return_address(0));
+}
+
+/*
+ * Do either kdump or normal kexec. In case of kdump we first ask
+ * purgatory, if kdump checksums are valid.
+ */
+void machine_kexec(struct kimage *image)
+{
+ if (image->type == KEXEC_TYPE_CRASH && !kdump_csum_valid(image))
+ return;
+ tracer_disable();
+ smp_send_stop();
+ smp_call_ipl_cpu(__machine_kexec, image);
+}
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S
new file mode 100644
index 00000000..7e2c38ba
--- /dev/null
+++ b/arch/s390/kernel/mcount.S
@@ -0,0 +1,71 @@
+/*
+ * Copyright IBM Corp. 2008,2009
+ *
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+
+ .section .kprobes.text, "ax"
+
+ENTRY(ftrace_stub)
+ br %r14
+
+ENTRY(_mcount)
+#ifdef CONFIG_DYNAMIC_FTRACE
+ br %r14
+
+ENTRY(ftrace_caller)
+#endif
+ stm %r2,%r5,16(%r15)
+ bras %r1,2f
+0: .long ftrace_trace_function
+1: .long function_trace_stop
+2: l %r2,1b-0b(%r1)
+ icm %r2,0xf,0(%r2)
+ jnz 3f
+ st %r14,56(%r15)
+ lr %r0,%r15
+ ahi %r15,-96
+ l %r3,100(%r15)
+ la %r2,0(%r14)
+ st %r0,__SF_BACKCHAIN(%r15)
+ la %r3,0(%r3)
+ l %r14,0b-0b(%r1)
+ l %r14,0(%r14)
+ basr %r14,%r14
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ l %r2,100(%r15)
+ l %r3,152(%r15)
+ENTRY(ftrace_graph_caller)
+# The bras instruction gets runtime patched to call prepare_ftrace_return.
+# See ftrace_enable_ftrace_graph_caller. The patched instruction is:
+# bras %r14,prepare_ftrace_return
+ bras %r14,0f
+0: st %r2,100(%r15)
+#endif
+ ahi %r15,96
+ l %r14,56(%r15)
+3: lm %r2,%r5,16(%r15)
+ br %r14
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+
+ENTRY(return_to_handler)
+ stm %r2,%r5,16(%r15)
+ st %r14,56(%r15)
+ lr %r0,%r15
+ ahi %r15,-96
+ st %r0,__SF_BACKCHAIN(%r15)
+ bras %r1,0f
+ .long ftrace_return_to_handler
+0: l %r2,0b-0b(%r1)
+ basr %r14,%r2
+ lr %r14,%r2
+ ahi %r15,96
+ lm %r2,%r5,16(%r15)
+ br %r14
+
+#endif
diff --git a/arch/s390/kernel/mcount64.S b/arch/s390/kernel/mcount64.S
new file mode 100644
index 00000000..f70cadec
--- /dev/null
+++ b/arch/s390/kernel/mcount64.S
@@ -0,0 +1,63 @@
+/*
+ * Copyright IBM Corp. 2008,2009
+ *
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+
+ .section .kprobes.text, "ax"
+
+ENTRY(ftrace_stub)
+ br %r14
+
+ENTRY(_mcount)
+#ifdef CONFIG_DYNAMIC_FTRACE
+ br %r14
+
+ENTRY(ftrace_caller)
+#endif
+ larl %r1,function_trace_stop
+ icm %r1,0xf,0(%r1)
+ bnzr %r14
+ stmg %r2,%r5,32(%r15)
+ stg %r14,112(%r15)
+ lgr %r1,%r15
+ aghi %r15,-160
+ stg %r1,__SF_BACKCHAIN(%r15)
+ lgr %r2,%r14
+ lg %r3,168(%r15)
+ larl %r14,ftrace_trace_function
+ lg %r14,0(%r14)
+ basr %r14,%r14
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+ lg %r2,168(%r15)
+ lg %r3,272(%r15)
+ENTRY(ftrace_graph_caller)
+# The bras instruction gets runtime patched to call prepare_ftrace_return.
+# See ftrace_enable_ftrace_graph_caller. The patched instruction is:
+# bras %r14,prepare_ftrace_return
+ bras %r14,0f
+0: stg %r2,168(%r15)
+#endif
+ aghi %r15,160
+ lmg %r2,%r5,32(%r15)
+ lg %r14,112(%r15)
+ br %r14
+
+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
+
+ENTRY(return_to_handler)
+ stmg %r2,%r5,32(%r15)
+ lgr %r1,%r15
+ aghi %r15,-160
+ stg %r1,__SF_BACKCHAIN(%r15)
+ brasl %r14,ftrace_return_to_handler
+ aghi %r15,160
+ lgr %r14,%r2
+ lmg %r2,%r5,32(%r15)
+ br %r14
+
+#endif
diff --git a/arch/s390/kernel/mem_detect.c b/arch/s390/kernel/mem_detect.c
new file mode 100644
index 00000000..22d502e8
--- /dev/null
+++ b/arch/s390/kernel/mem_detect.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright IBM Corp. 2008, 2009
+ *
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/ipl.h>
+#include <asm/sclp.h>
+#include <asm/setup.h>
+
+#define ADDR2G (1ULL << 31)
+
+static void find_memory_chunks(struct mem_chunk chunk[])
+{
+ unsigned long long memsize, rnmax, rzm;
+ unsigned long addr = 0, size;
+ int i = 0, type;
+
+ rzm = sclp_get_rzm();
+ rnmax = sclp_get_rnmax();
+ memsize = rzm * rnmax;
+ if (!rzm)
+ rzm = 1ULL << 17;
+ if (sizeof(long) == 4) {
+ rzm = min(ADDR2G, rzm);
+ memsize = memsize ? min(ADDR2G, memsize) : ADDR2G;
+ }
+ do {
+ size = 0;
+ type = tprot(addr);
+ do {
+ size += rzm;
+ if (memsize && addr + size >= memsize)
+ break;
+ } while (type == tprot(addr + size));
+ if (type == CHUNK_READ_WRITE || type == CHUNK_READ_ONLY) {
+ chunk[i].addr = addr;
+ chunk[i].size = size;
+ chunk[i].type = type;
+ i++;
+ }
+ addr += size;
+ } while (addr < memsize && i < MEMORY_CHUNKS);
+}
+
+void detect_memory_layout(struct mem_chunk chunk[])
+{
+ unsigned long flags, cr0;
+
+ memset(chunk, 0, MEMORY_CHUNKS * sizeof(struct mem_chunk));
+ /* Disable IRQs, DAT and low address protection so tprot does the
+ * right thing and we don't get scheduled away with low address
+ * protection disabled.
+ */
+ flags = __arch_local_irq_stnsm(0xf8);
+ __ctl_store(cr0, 0, 0);
+ __ctl_clear_bit(0, 28);
+ find_memory_chunks(chunk);
+ __ctl_load(cr0, 0, 0);
+ arch_local_irq_restore(flags);
+}
+EXPORT_SYMBOL(detect_memory_layout);
+
+/*
+ * Move memory chunks array from index "from" to index "to"
+ */
+static void mem_chunk_move(struct mem_chunk chunk[], int to, int from)
+{
+ int cnt = MEMORY_CHUNKS - to;
+
+ memmove(&chunk[to], &chunk[from], cnt * sizeof(struct mem_chunk));
+}
+
+/*
+ * Initialize memory chunk
+ */
+static void mem_chunk_init(struct mem_chunk *chunk, unsigned long addr,
+ unsigned long size, int type)
+{
+ chunk->type = type;
+ chunk->addr = addr;
+ chunk->size = size;
+}
+
+/*
+ * Create memory hole with given address, size, and type
+ */
+void create_mem_hole(struct mem_chunk chunk[], unsigned long addr,
+ unsigned long size, int type)
+{
+ unsigned long lh_start, lh_end, lh_size, ch_start, ch_end, ch_size;
+ int i, ch_type;
+
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ if (chunk[i].size == 0)
+ continue;
+
+ /* Define chunk properties */
+ ch_start = chunk[i].addr;
+ ch_size = chunk[i].size;
+ ch_end = ch_start + ch_size - 1;
+ ch_type = chunk[i].type;
+
+ /* Is memory chunk hit by memory hole? */
+ if (addr + size <= ch_start)
+ continue; /* No: memory hole in front of chunk */
+ if (addr > ch_end)
+ continue; /* No: memory hole after chunk */
+
+ /* Yes: Define local hole properties */
+ lh_start = max(addr, chunk[i].addr);
+ lh_end = min(addr + size - 1, ch_end);
+ lh_size = lh_end - lh_start + 1;
+
+ if (lh_start == ch_start && lh_end == ch_end) {
+ /* Hole covers complete memory chunk */
+ mem_chunk_init(&chunk[i], lh_start, lh_size, type);
+ } else if (lh_end == ch_end) {
+ /* Hole starts in memory chunk and convers chunk end */
+ mem_chunk_move(chunk, i + 1, i);
+ mem_chunk_init(&chunk[i], ch_start, ch_size - lh_size,
+ ch_type);
+ mem_chunk_init(&chunk[i + 1], lh_start, lh_size, type);
+ i += 1;
+ } else if (lh_start == ch_start) {
+ /* Hole ends in memory chunk */
+ mem_chunk_move(chunk, i + 1, i);
+ mem_chunk_init(&chunk[i], lh_start, lh_size, type);
+ mem_chunk_init(&chunk[i + 1], lh_end + 1,
+ ch_size - lh_size, ch_type);
+ break;
+ } else {
+ /* Hole splits memory chunk */
+ mem_chunk_move(chunk, i + 2, i);
+ mem_chunk_init(&chunk[i], ch_start,
+ lh_start - ch_start, ch_type);
+ mem_chunk_init(&chunk[i + 1], lh_start, lh_size, type);
+ mem_chunk_init(&chunk[i + 2], lh_end + 1,
+ ch_end - lh_end, ch_type);
+ break;
+ }
+ }
+}
diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c
new file mode 100644
index 00000000..dfcb3436
--- /dev/null
+++ b/arch/s390/kernel/module.c
@@ -0,0 +1,395 @@
+/*
+ * arch/s390/kernel/module.c - Kernel module help for s390.
+ *
+ * S390 version
+ * Copyright (C) 2002, 2003 IBM Deutschland Entwicklung GmbH,
+ * IBM Corporation
+ * Author(s): Arnd Bergmann (arndb@de.ibm.com)
+ * Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ * based on i386 version
+ * Copyright (C) 2001 Rusty Russell.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/module.h>
+#include <linux/elf.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/moduleloader.h>
+#include <linux/bug.h>
+
+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(fmt , ...)
+#endif
+
+#ifndef CONFIG_64BIT
+#define PLT_ENTRY_SIZE 12
+#else /* CONFIG_64BIT */
+#define PLT_ENTRY_SIZE 20
+#endif /* CONFIG_64BIT */
+
+/* Free memory returned from module_alloc */
+void module_free(struct module *mod, void *module_region)
+{
+ if (mod) {
+ vfree(mod->arch.syminfo);
+ mod->arch.syminfo = NULL;
+ }
+ vfree(module_region);
+}
+
+static void
+check_rela(Elf_Rela *rela, struct module *me)
+{
+ struct mod_arch_syminfo *info;
+
+ info = me->arch.syminfo + ELF_R_SYM (rela->r_info);
+ switch (ELF_R_TYPE (rela->r_info)) {
+ case R_390_GOT12: /* 12 bit GOT offset. */
+ case R_390_GOT16: /* 16 bit GOT offset. */
+ case R_390_GOT20: /* 20 bit GOT offset. */
+ case R_390_GOT32: /* 32 bit GOT offset. */
+ case R_390_GOT64: /* 64 bit GOT offset. */
+ case R_390_GOTENT: /* 32 bit PC rel. to GOT entry shifted by 1. */
+ case R_390_GOTPLT12: /* 12 bit offset to jump slot. */
+ case R_390_GOTPLT16: /* 16 bit offset to jump slot. */
+ case R_390_GOTPLT20: /* 20 bit offset to jump slot. */
+ case R_390_GOTPLT32: /* 32 bit offset to jump slot. */
+ case R_390_GOTPLT64: /* 64 bit offset to jump slot. */
+ case R_390_GOTPLTENT: /* 32 bit rel. offset to jump slot >> 1. */
+ if (info->got_offset == -1UL) {
+ info->got_offset = me->arch.got_size;
+ me->arch.got_size += sizeof(void*);
+ }
+ break;
+ case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */
+ case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */
+ case R_390_PLT32: /* 32 bit PC relative PLT address. */
+ case R_390_PLT64: /* 64 bit PC relative PLT address. */
+ case R_390_PLTOFF16: /* 16 bit offset from GOT to PLT. */
+ case R_390_PLTOFF32: /* 32 bit offset from GOT to PLT. */
+ case R_390_PLTOFF64: /* 16 bit offset from GOT to PLT. */
+ if (info->plt_offset == -1UL) {
+ info->plt_offset = me->arch.plt_size;
+ me->arch.plt_size += PLT_ENTRY_SIZE;
+ }
+ break;
+ case R_390_COPY:
+ case R_390_GLOB_DAT:
+ case R_390_JMP_SLOT:
+ case R_390_RELATIVE:
+ /* Only needed if we want to support loading of
+ modules linked with -shared. */
+ break;
+ }
+}
+
+/*
+ * Account for GOT and PLT relocations. We can't add sections for
+ * got and plt but we can increase the core module size.
+ */
+int
+module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
+ char *secstrings, struct module *me)
+{
+ Elf_Shdr *symtab;
+ Elf_Sym *symbols;
+ Elf_Rela *rela;
+ char *strings;
+ int nrela, i, j;
+
+ /* Find symbol table and string table. */
+ symtab = NULL;
+ for (i = 0; i < hdr->e_shnum; i++)
+ switch (sechdrs[i].sh_type) {
+ case SHT_SYMTAB:
+ symtab = sechdrs + i;
+ break;
+ }
+ if (!symtab) {
+ printk(KERN_ERR "module %s: no symbol table\n", me->name);
+ return -ENOEXEC;
+ }
+
+ /* Allocate one syminfo structure per symbol. */
+ me->arch.nsyms = symtab->sh_size / sizeof(Elf_Sym);
+ me->arch.syminfo = vmalloc(me->arch.nsyms *
+ sizeof(struct mod_arch_syminfo));
+ if (!me->arch.syminfo)
+ return -ENOMEM;
+ symbols = (void *) hdr + symtab->sh_offset;
+ strings = (void *) hdr + sechdrs[symtab->sh_link].sh_offset;
+ for (i = 0; i < me->arch.nsyms; i++) {
+ if (symbols[i].st_shndx == SHN_UNDEF &&
+ strcmp(strings + symbols[i].st_name,
+ "_GLOBAL_OFFSET_TABLE_") == 0)
+ /* "Define" it as absolute. */
+ symbols[i].st_shndx = SHN_ABS;
+ me->arch.syminfo[i].got_offset = -1UL;
+ me->arch.syminfo[i].plt_offset = -1UL;
+ me->arch.syminfo[i].got_initialized = 0;
+ me->arch.syminfo[i].plt_initialized = 0;
+ }
+
+ /* Search for got/plt relocations. */
+ me->arch.got_size = me->arch.plt_size = 0;
+ for (i = 0; i < hdr->e_shnum; i++) {
+ if (sechdrs[i].sh_type != SHT_RELA)
+ continue;
+ nrela = sechdrs[i].sh_size / sizeof(Elf_Rela);
+ rela = (void *) hdr + sechdrs[i].sh_offset;
+ for (j = 0; j < nrela; j++)
+ check_rela(rela + j, me);
+ }
+
+ /* Increase core size by size of got & plt and set start
+ offsets for got and plt. */
+ me->core_size = ALIGN(me->core_size, 4);
+ me->arch.got_offset = me->core_size;
+ me->core_size += me->arch.got_size;
+ me->arch.plt_offset = me->core_size;
+ me->core_size += me->arch.plt_size;
+ return 0;
+}
+
+static int
+apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
+ struct module *me)
+{
+ struct mod_arch_syminfo *info;
+ Elf_Addr loc, val;
+ int r_type, r_sym;
+
+ /* This is where to make the change */
+ loc = base + rela->r_offset;
+ /* This is the symbol it is referring to. Note that all
+ undefined symbols have been resolved. */
+ r_sym = ELF_R_SYM(rela->r_info);
+ r_type = ELF_R_TYPE(rela->r_info);
+ info = me->arch.syminfo + r_sym;
+ val = symtab[r_sym].st_value;
+
+ switch (r_type) {
+ case R_390_8: /* Direct 8 bit. */
+ case R_390_12: /* Direct 12 bit. */
+ case R_390_16: /* Direct 16 bit. */
+ case R_390_20: /* Direct 20 bit. */
+ case R_390_32: /* Direct 32 bit. */
+ case R_390_64: /* Direct 64 bit. */
+ val += rela->r_addend;
+ if (r_type == R_390_8)
+ *(unsigned char *) loc = val;
+ else if (r_type == R_390_12)
+ *(unsigned short *) loc = (val & 0xfff) |
+ (*(unsigned short *) loc & 0xf000);
+ else if (r_type == R_390_16)
+ *(unsigned short *) loc = val;
+ else if (r_type == R_390_20)
+ *(unsigned int *) loc =
+ (*(unsigned int *) loc & 0xf00000ff) |
+ (val & 0xfff) << 16 | (val & 0xff000) >> 4;
+ else if (r_type == R_390_32)
+ *(unsigned int *) loc = val;
+ else if (r_type == R_390_64)
+ *(unsigned long *) loc = val;
+ break;
+ case R_390_PC16: /* PC relative 16 bit. */
+ case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */
+ case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */
+ case R_390_PC32: /* PC relative 32 bit. */
+ case R_390_PC64: /* PC relative 64 bit. */
+ val += rela->r_addend - loc;
+ if (r_type == R_390_PC16)
+ *(unsigned short *) loc = val;
+ else if (r_type == R_390_PC16DBL)
+ *(unsigned short *) loc = val >> 1;
+ else if (r_type == R_390_PC32DBL)
+ *(unsigned int *) loc = val >> 1;
+ else if (r_type == R_390_PC32)
+ *(unsigned int *) loc = val;
+ else if (r_type == R_390_PC64)
+ *(unsigned long *) loc = val;
+ break;
+ case R_390_GOT12: /* 12 bit GOT offset. */
+ case R_390_GOT16: /* 16 bit GOT offset. */
+ case R_390_GOT20: /* 20 bit GOT offset. */
+ case R_390_GOT32: /* 32 bit GOT offset. */
+ case R_390_GOT64: /* 64 bit GOT offset. */
+ case R_390_GOTENT: /* 32 bit PC rel. to GOT entry shifted by 1. */
+ case R_390_GOTPLT12: /* 12 bit offset to jump slot. */
+ case R_390_GOTPLT20: /* 20 bit offset to jump slot. */
+ case R_390_GOTPLT16: /* 16 bit offset to jump slot. */
+ case R_390_GOTPLT32: /* 32 bit offset to jump slot. */
+ case R_390_GOTPLT64: /* 64 bit offset to jump slot. */
+ case R_390_GOTPLTENT: /* 32 bit rel. offset to jump slot >> 1. */
+ if (info->got_initialized == 0) {
+ Elf_Addr *gotent;
+
+ gotent = me->module_core + me->arch.got_offset +
+ info->got_offset;
+ *gotent = val;
+ info->got_initialized = 1;
+ }
+ val = info->got_offset + rela->r_addend;
+ if (r_type == R_390_GOT12 ||
+ r_type == R_390_GOTPLT12)
+ *(unsigned short *) loc = (val & 0xfff) |
+ (*(unsigned short *) loc & 0xf000);
+ else if (r_type == R_390_GOT16 ||
+ r_type == R_390_GOTPLT16)
+ *(unsigned short *) loc = val;
+ else if (r_type == R_390_GOT20 ||
+ r_type == R_390_GOTPLT20)
+ *(unsigned int *) loc =
+ (*(unsigned int *) loc & 0xf00000ff) |
+ (val & 0xfff) << 16 | (val & 0xff000) >> 4;
+ else if (r_type == R_390_GOT32 ||
+ r_type == R_390_GOTPLT32)
+ *(unsigned int *) loc = val;
+ else if (r_type == R_390_GOTENT ||
+ r_type == R_390_GOTPLTENT)
+ *(unsigned int *) loc =
+ (val + (Elf_Addr) me->module_core - loc) >> 1;
+ else if (r_type == R_390_GOT64 ||
+ r_type == R_390_GOTPLT64)
+ *(unsigned long *) loc = val;
+ break;
+ case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */
+ case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */
+ case R_390_PLT32: /* 32 bit PC relative PLT address. */
+ case R_390_PLT64: /* 64 bit PC relative PLT address. */
+ case R_390_PLTOFF16: /* 16 bit offset from GOT to PLT. */
+ case R_390_PLTOFF32: /* 32 bit offset from GOT to PLT. */
+ case R_390_PLTOFF64: /* 16 bit offset from GOT to PLT. */
+ if (info->plt_initialized == 0) {
+ unsigned int *ip;
+ ip = me->module_core + me->arch.plt_offset +
+ info->plt_offset;
+#ifndef CONFIG_64BIT
+ ip[0] = 0x0d105810; /* basr 1,0; l 1,6(1); br 1 */
+ ip[1] = 0x100607f1;
+ ip[2] = val;
+#else /* CONFIG_64BIT */
+ ip[0] = 0x0d10e310; /* basr 1,0; lg 1,10(1); br 1 */
+ ip[1] = 0x100a0004;
+ ip[2] = 0x07f10000;
+ ip[3] = (unsigned int) (val >> 32);
+ ip[4] = (unsigned int) val;
+#endif /* CONFIG_64BIT */
+ info->plt_initialized = 1;
+ }
+ if (r_type == R_390_PLTOFF16 ||
+ r_type == R_390_PLTOFF32 ||
+ r_type == R_390_PLTOFF64)
+ val = me->arch.plt_offset - me->arch.got_offset +
+ info->plt_offset + rela->r_addend;
+ else {
+ if (!((r_type == R_390_PLT16DBL &&
+ val - loc + 0xffffUL < 0x1ffffeUL) ||
+ (r_type == R_390_PLT32DBL &&
+ val - loc + 0xffffffffULL < 0x1fffffffeULL)))
+ val = (Elf_Addr) me->module_core +
+ me->arch.plt_offset +
+ info->plt_offset;
+ val += rela->r_addend - loc;
+ }
+ if (r_type == R_390_PLT16DBL)
+ *(unsigned short *) loc = val >> 1;
+ else if (r_type == R_390_PLTOFF16)
+ *(unsigned short *) loc = val;
+ else if (r_type == R_390_PLT32DBL)
+ *(unsigned int *) loc = val >> 1;
+ else if (r_type == R_390_PLT32 ||
+ r_type == R_390_PLTOFF32)
+ *(unsigned int *) loc = val;
+ else if (r_type == R_390_PLT64 ||
+ r_type == R_390_PLTOFF64)
+ *(unsigned long *) loc = val;
+ break;
+ case R_390_GOTOFF16: /* 16 bit offset to GOT. */
+ case R_390_GOTOFF32: /* 32 bit offset to GOT. */
+ case R_390_GOTOFF64: /* 64 bit offset to GOT. */
+ val = val + rela->r_addend -
+ ((Elf_Addr) me->module_core + me->arch.got_offset);
+ if (r_type == R_390_GOTOFF16)
+ *(unsigned short *) loc = val;
+ else if (r_type == R_390_GOTOFF32)
+ *(unsigned int *) loc = val;
+ else if (r_type == R_390_GOTOFF64)
+ *(unsigned long *) loc = val;
+ break;
+ case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */
+ case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */
+ val = (Elf_Addr) me->module_core + me->arch.got_offset +
+ rela->r_addend - loc;
+ if (r_type == R_390_GOTPC)
+ *(unsigned int *) loc = val;
+ else if (r_type == R_390_GOTPCDBL)
+ *(unsigned int *) loc = val >> 1;
+ break;
+ case R_390_COPY:
+ case R_390_GLOB_DAT: /* Create GOT entry. */
+ case R_390_JMP_SLOT: /* Create PLT entry. */
+ case R_390_RELATIVE: /* Adjust by program base. */
+ /* Only needed if we want to support loading of
+ modules linked with -shared. */
+ break;
+ default:
+ printk(KERN_ERR "module %s: Unknown relocation: %u\n",
+ me->name, r_type);
+ return -ENOEXEC;
+ }
+ return 0;
+}
+
+int
+apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
+ unsigned int symindex, unsigned int relsec,
+ struct module *me)
+{
+ Elf_Addr base;
+ Elf_Sym *symtab;
+ Elf_Rela *rela;
+ unsigned long i, n;
+ int rc;
+
+ DEBUGP("Applying relocate section %u to %u\n",
+ relsec, sechdrs[relsec].sh_info);
+ base = sechdrs[sechdrs[relsec].sh_info].sh_addr;
+ symtab = (Elf_Sym *) sechdrs[symindex].sh_addr;
+ rela = (Elf_Rela *) sechdrs[relsec].sh_addr;
+ n = sechdrs[relsec].sh_size / sizeof(Elf_Rela);
+
+ for (i = 0; i < n; i++, rela++) {
+ rc = apply_rela(rela, base, symtab, me);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+int module_finalize(const Elf_Ehdr *hdr,
+ const Elf_Shdr *sechdrs,
+ struct module *me)
+{
+ vfree(me->arch.syminfo);
+ me->arch.syminfo = NULL;
+ return 0;
+}
diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c
new file mode 100644
index 00000000..8c372ca6
--- /dev/null
+++ b/arch/s390/kernel/nmi.c
@@ -0,0 +1,375 @@
+/*
+ * Machine check handler
+ *
+ * Copyright IBM Corp. 2000,2009
+ * Author(s): Ingo Adlung <adlung@de.ibm.com>,
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>,
+ * Cornelia Huck <cornelia.huck@de.ibm.com>,
+ * Heiko Carstens <heiko.carstens@de.ibm.com>,
+ */
+
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/hardirq.h>
+#include <linux/time.h>
+#include <linux/module.h>
+#include <asm/lowcore.h>
+#include <asm/smp.h>
+#include <asm/etr.h>
+#include <asm/cputime.h>
+#include <asm/nmi.h>
+#include <asm/crw.h>
+
+struct mcck_struct {
+ int kill_task;
+ int channel_report;
+ int warning;
+ unsigned long long mcck_code;
+};
+
+static DEFINE_PER_CPU(struct mcck_struct, cpu_mcck);
+
+static void s390_handle_damage(char *msg)
+{
+ smp_send_stop();
+ disabled_wait((unsigned long) __builtin_return_address(0));
+ while (1);
+}
+
+/*
+ * Main machine check handler function. Will be called with interrupts enabled
+ * or disabled and machine checks enabled or disabled.
+ */
+void s390_handle_mcck(void)
+{
+ unsigned long flags;
+ struct mcck_struct mcck;
+
+ /*
+ * Disable machine checks and get the current state of accumulated
+ * machine checks. Afterwards delete the old state and enable machine
+ * checks again.
+ */
+ local_irq_save(flags);
+ local_mcck_disable();
+ mcck = __get_cpu_var(cpu_mcck);
+ memset(&__get_cpu_var(cpu_mcck), 0, sizeof(struct mcck_struct));
+ clear_thread_flag(TIF_MCCK_PENDING);
+ local_mcck_enable();
+ local_irq_restore(flags);
+
+ if (mcck.channel_report)
+ crw_handle_channel_report();
+ /*
+ * A warning may remain for a prolonged period on the bare iron.
+ * (actually until the machine is powered off, or the problem is gone)
+ * So we just stop listening for the WARNING MCH and avoid continuously
+ * being interrupted. One caveat is however, that we must do this per
+ * processor and cannot use the smp version of ctl_clear_bit().
+ * On VM we only get one interrupt per virtally presented machinecheck.
+ * Though one suffices, we may get one interrupt per (virtual) cpu.
+ */
+ if (mcck.warning) { /* WARNING pending ? */
+ static int mchchk_wng_posted = 0;
+
+ /* Use single cpu clear, as we cannot handle smp here. */
+ __ctl_clear_bit(14, 24); /* Disable WARNING MCH */
+ if (xchg(&mchchk_wng_posted, 1) == 0)
+ kill_cad_pid(SIGPWR, 1);
+ }
+ if (mcck.kill_task) {
+ local_irq_enable();
+ printk(KERN_EMERG "mcck: Terminating task because of machine "
+ "malfunction (code 0x%016llx).\n", mcck.mcck_code);
+ printk(KERN_EMERG "mcck: task: %s, pid: %d.\n",
+ current->comm, current->pid);
+ do_exit(SIGSEGV);
+ }
+}
+EXPORT_SYMBOL_GPL(s390_handle_mcck);
+
+/*
+ * returns 0 if all registers could be validated
+ * returns 1 otherwise
+ */
+static int notrace s390_revalidate_registers(struct mci *mci)
+{
+ int kill_task;
+ u64 zero;
+ void *fpt_save_area, *fpt_creg_save_area;
+
+ kill_task = 0;
+ zero = 0;
+
+ if (!mci->gr) {
+ /*
+ * General purpose registers couldn't be restored and have
+ * unknown contents. Process needs to be terminated.
+ */
+ kill_task = 1;
+ }
+ if (!mci->fp) {
+ /*
+ * Floating point registers can't be restored and
+ * therefore the process needs to be terminated.
+ */
+ kill_task = 1;
+ }
+#ifndef CONFIG_64BIT
+ asm volatile(
+ " ld 0,0(%0)\n"
+ " ld 2,8(%0)\n"
+ " ld 4,16(%0)\n"
+ " ld 6,24(%0)"
+ : : "a" (&S390_lowcore.floating_pt_save_area));
+#endif
+
+ if (MACHINE_HAS_IEEE) {
+#ifdef CONFIG_64BIT
+ fpt_save_area = &S390_lowcore.floating_pt_save_area;
+ fpt_creg_save_area = &S390_lowcore.fpt_creg_save_area;
+#else
+ fpt_save_area = (void *) S390_lowcore.extended_save_area_addr;
+ fpt_creg_save_area = fpt_save_area + 128;
+#endif
+ if (!mci->fc) {
+ /*
+ * Floating point control register can't be restored.
+ * Task will be terminated.
+ */
+ asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
+ kill_task = 1;
+
+ } else
+ asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area));
+
+ asm volatile(
+ " ld 0,0(%0)\n"
+ " ld 1,8(%0)\n"
+ " ld 2,16(%0)\n"
+ " ld 3,24(%0)\n"
+ " ld 4,32(%0)\n"
+ " ld 5,40(%0)\n"
+ " ld 6,48(%0)\n"
+ " ld 7,56(%0)\n"
+ " ld 8,64(%0)\n"
+ " ld 9,72(%0)\n"
+ " ld 10,80(%0)\n"
+ " ld 11,88(%0)\n"
+ " ld 12,96(%0)\n"
+ " ld 13,104(%0)\n"
+ " ld 14,112(%0)\n"
+ " ld 15,120(%0)\n"
+ : : "a" (fpt_save_area));
+ }
+ /* Revalidate access registers */
+ asm volatile(
+ " lam 0,15,0(%0)"
+ : : "a" (&S390_lowcore.access_regs_save_area));
+ if (!mci->ar) {
+ /*
+ * Access registers have unknown contents.
+ * Terminating task.
+ */
+ kill_task = 1;
+ }
+ /* Revalidate control registers */
+ if (!mci->cr) {
+ /*
+ * Control registers have unknown contents.
+ * Can't recover and therefore stopping machine.
+ */
+ s390_handle_damage("invalid control registers.");
+ } else {
+#ifdef CONFIG_64BIT
+ asm volatile(
+ " lctlg 0,15,0(%0)"
+ : : "a" (&S390_lowcore.cregs_save_area));
+#else
+ asm volatile(
+ " lctl 0,15,0(%0)"
+ : : "a" (&S390_lowcore.cregs_save_area));
+#endif
+ }
+ /*
+ * We don't even try to revalidate the TOD register, since we simply
+ * can't write something sensible into that register.
+ */
+#ifdef CONFIG_64BIT
+ /*
+ * See if we can revalidate the TOD programmable register with its
+ * old contents (should be zero) otherwise set it to zero.
+ */
+ if (!mci->pr)
+ asm volatile(
+ " sr 0,0\n"
+ " sckpf"
+ : : : "0", "cc");
+ else
+ asm volatile(
+ " l 0,0(%0)\n"
+ " sckpf"
+ : : "a" (&S390_lowcore.tod_progreg_save_area)
+ : "0", "cc");
+#endif
+ /* Revalidate clock comparator register */
+ if (S390_lowcore.clock_comparator == -1)
+ set_clock_comparator(S390_lowcore.mcck_clock);
+ else
+ set_clock_comparator(S390_lowcore.clock_comparator);
+ /* Check if old PSW is valid */
+ if (!mci->wp)
+ /*
+ * Can't tell if we come from user or kernel mode
+ * -> stopping machine.
+ */
+ s390_handle_damage("old psw invalid.");
+
+ if (!mci->ms || !mci->pm || !mci->ia)
+ kill_task = 1;
+
+ return kill_task;
+}
+
+#define MAX_IPD_COUNT 29
+#define MAX_IPD_TIME (5 * 60 * USEC_PER_SEC) /* 5 minutes */
+
+#define ED_STP_ISLAND 6 /* External damage STP island check */
+#define ED_STP_SYNC 7 /* External damage STP sync check */
+#define ED_ETR_SYNC 12 /* External damage ETR sync check */
+#define ED_ETR_SWITCH 13 /* External damage ETR switch to local */
+
+/*
+ * machine check handler.
+ */
+void notrace s390_do_machine_check(struct pt_regs *regs)
+{
+ static int ipd_count;
+ static DEFINE_SPINLOCK(ipd_lock);
+ static unsigned long long last_ipd;
+ struct mcck_struct *mcck;
+ unsigned long long tmp;
+ struct mci *mci;
+ int umode;
+
+ nmi_enter();
+ kstat_cpu(smp_processor_id()).irqs[NMI_NMI]++;
+ mci = (struct mci *) &S390_lowcore.mcck_interruption_code;
+ mcck = &__get_cpu_var(cpu_mcck);
+ umode = user_mode(regs);
+
+ if (mci->sd) {
+ /* System damage -> stopping machine */
+ s390_handle_damage("received system damage machine check.");
+ }
+ if (mci->pd) {
+ if (mci->b) {
+ /* Processing backup -> verify if we can survive this */
+ u64 z_mcic, o_mcic, t_mcic;
+#ifdef CONFIG_64BIT
+ z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<29);
+ o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 |
+ 1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 |
+ 1ULL<<30 | 1ULL<<21 | 1ULL<<20 | 1ULL<<17 |
+ 1ULL<<16);
+#else
+ z_mcic = (1ULL<<63 | 1ULL<<59 | 1ULL<<57 | 1ULL<<50 |
+ 1ULL<<29);
+ o_mcic = (1ULL<<43 | 1ULL<<42 | 1ULL<<41 | 1ULL<<40 |
+ 1ULL<<36 | 1ULL<<35 | 1ULL<<34 | 1ULL<<32 |
+ 1ULL<<30 | 1ULL<<20 | 1ULL<<17 | 1ULL<<16);
+#endif
+ t_mcic = *(u64 *)mci;
+
+ if (((t_mcic & z_mcic) != 0) ||
+ ((t_mcic & o_mcic) != o_mcic)) {
+ s390_handle_damage("processing backup machine "
+ "check with damage.");
+ }
+
+ /*
+ * Nullifying exigent condition, therefore we might
+ * retry this instruction.
+ */
+ spin_lock(&ipd_lock);
+ tmp = get_clock();
+ if (((tmp - last_ipd) >> 12) < MAX_IPD_TIME)
+ ipd_count++;
+ else
+ ipd_count = 1;
+ last_ipd = tmp;
+ if (ipd_count == MAX_IPD_COUNT)
+ s390_handle_damage("too many ipd retries.");
+ spin_unlock(&ipd_lock);
+ } else {
+ /* Processing damage -> stopping machine */
+ s390_handle_damage("received instruction processing "
+ "damage machine check.");
+ }
+ }
+ if (s390_revalidate_registers(mci)) {
+ if (umode) {
+ /*
+ * Couldn't restore all register contents while in
+ * user mode -> mark task for termination.
+ */
+ mcck->kill_task = 1;
+ mcck->mcck_code = *(unsigned long long *) mci;
+ set_thread_flag(TIF_MCCK_PENDING);
+ } else {
+ /*
+ * Couldn't restore all register contents while in
+ * kernel mode -> stopping machine.
+ */
+ s390_handle_damage("unable to revalidate registers.");
+ }
+ }
+ if (mci->cd) {
+ /* Timing facility damage */
+ s390_handle_damage("TOD clock damaged");
+ }
+ if (mci->ed && mci->ec) {
+ /* External damage */
+ if (S390_lowcore.external_damage_code & (1U << ED_ETR_SYNC))
+ etr_sync_check();
+ if (S390_lowcore.external_damage_code & (1U << ED_ETR_SWITCH))
+ etr_switch_to_local();
+ if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC))
+ stp_sync_check();
+ if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND))
+ stp_island_check();
+ }
+ if (mci->se)
+ /* Storage error uncorrected */
+ s390_handle_damage("received storage error uncorrected "
+ "machine check.");
+ if (mci->ke)
+ /* Storage key-error uncorrected */
+ s390_handle_damage("received storage key-error uncorrected "
+ "machine check.");
+ if (mci->ds && mci->fa)
+ /* Storage degradation */
+ s390_handle_damage("received storage degradation machine "
+ "check.");
+ if (mci->cp) {
+ /* Channel report word pending */
+ mcck->channel_report = 1;
+ set_thread_flag(TIF_MCCK_PENDING);
+ }
+ if (mci->w) {
+ /* Warning pending */
+ mcck->warning = 1;
+ set_thread_flag(TIF_MCCK_PENDING);
+ }
+ nmi_exit();
+}
+
+static int __init machine_check_init(void)
+{
+ ctl_set_bit(14, 25); /* enable external damage MCH */
+ ctl_set_bit(14, 27); /* enable system recovery MCH */
+ ctl_set_bit(14, 24); /* enable warning MCH */
+ return 0;
+}
+arch_initcall(machine_check_init);
diff --git a/arch/s390/kernel/os_info.c b/arch/s390/kernel/os_info.c
new file mode 100644
index 00000000..e8d6c214
--- /dev/null
+++ b/arch/s390/kernel/os_info.c
@@ -0,0 +1,168 @@
+/*
+ * OS info memory interface
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#define KMSG_COMPONENT "os_info"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/crash_dump.h>
+#include <linux/kernel.h>
+#include <asm/checksum.h>
+#include <asm/lowcore.h>
+#include <asm/os_info.h>
+
+/*
+ * OS info structure has to be page aligned
+ */
+static struct os_info os_info __page_aligned_data;
+
+/*
+ * Compute checksum over OS info structure
+ */
+u32 os_info_csum(struct os_info *os_info)
+{
+ int size = sizeof(*os_info) - offsetof(struct os_info, version_major);
+ return csum_partial(&os_info->version_major, size, 0);
+}
+
+/*
+ * Add crashkernel info to OS info and update checksum
+ */
+void os_info_crashkernel_add(unsigned long base, unsigned long size)
+{
+ os_info.crashkernel_addr = (u64)(unsigned long)base;
+ os_info.crashkernel_size = (u64)(unsigned long)size;
+ os_info.csum = os_info_csum(&os_info);
+}
+
+/*
+ * Add OS info entry and update checksum
+ */
+void os_info_entry_add(int nr, void *ptr, u64 size)
+{
+ os_info.entry[nr].addr = (u64)(unsigned long)ptr;
+ os_info.entry[nr].size = size;
+ os_info.entry[nr].csum = csum_partial(ptr, size, 0);
+ os_info.csum = os_info_csum(&os_info);
+}
+
+/*
+ * Initialize OS info struture and set lowcore pointer
+ */
+void __init os_info_init(void)
+{
+ void *ptr = &os_info;
+
+ os_info.version_major = OS_INFO_VERSION_MAJOR;
+ os_info.version_minor = OS_INFO_VERSION_MINOR;
+ os_info.magic = OS_INFO_MAGIC;
+ os_info.csum = os_info_csum(&os_info);
+ copy_to_absolute_zero(&S390_lowcore.os_info, &ptr, sizeof(ptr));
+}
+
+#ifdef CONFIG_CRASH_DUMP
+
+static struct os_info *os_info_old;
+
+/*
+ * Allocate and copy OS info entry from oldmem
+ */
+static void os_info_old_alloc(int nr, int align)
+{
+ unsigned long addr, size = 0;
+ char *buf, *buf_align, *msg;
+ u32 csum;
+
+ addr = os_info_old->entry[nr].addr;
+ if (!addr) {
+ msg = "not available";
+ goto fail;
+ }
+ size = os_info_old->entry[nr].size;
+ buf = kmalloc(size + align - 1, GFP_KERNEL);
+ if (!buf) {
+ msg = "alloc failed";
+ goto fail;
+ }
+ buf_align = PTR_ALIGN(buf, align);
+ if (copy_from_oldmem(buf_align, (void *) addr, size)) {
+ msg = "copy failed";
+ goto fail_free;
+ }
+ csum = csum_partial(buf_align, size, 0);
+ if (csum != os_info_old->entry[nr].csum) {
+ msg = "checksum failed";
+ goto fail_free;
+ }
+ os_info_old->entry[nr].addr = (u64)(unsigned long)buf_align;
+ msg = "copied";
+ goto out;
+fail_free:
+ kfree(buf);
+fail:
+ os_info_old->entry[nr].addr = 0;
+out:
+ pr_info("entry %i: %s (addr=0x%lx size=%lu)\n",
+ nr, msg, addr, size);
+}
+
+/*
+ * Initialize os info and os info entries from oldmem
+ */
+static void os_info_old_init(void)
+{
+ static int os_info_init;
+ unsigned long addr;
+
+ if (os_info_init)
+ return;
+ if (!OLDMEM_BASE)
+ goto fail;
+ if (copy_from_oldmem(&addr, &S390_lowcore.os_info, sizeof(addr)))
+ goto fail;
+ if (addr == 0 || addr % PAGE_SIZE)
+ goto fail;
+ os_info_old = kzalloc(sizeof(*os_info_old), GFP_KERNEL);
+ if (!os_info_old)
+ goto fail;
+ if (copy_from_oldmem(os_info_old, (void *) addr, sizeof(*os_info_old)))
+ goto fail_free;
+ if (os_info_old->magic != OS_INFO_MAGIC)
+ goto fail_free;
+ if (os_info_old->csum != os_info_csum(os_info_old))
+ goto fail_free;
+ if (os_info_old->version_major > OS_INFO_VERSION_MAJOR)
+ goto fail_free;
+ os_info_old_alloc(OS_INFO_VMCOREINFO, 1);
+ os_info_old_alloc(OS_INFO_REIPL_BLOCK, 1);
+ os_info_old_alloc(OS_INFO_INIT_FN, PAGE_SIZE);
+ pr_info("crashkernel: addr=0x%lx size=%lu\n",
+ (unsigned long) os_info_old->crashkernel_addr,
+ (unsigned long) os_info_old->crashkernel_size);
+ os_info_init = 1;
+ return;
+fail_free:
+ kfree(os_info_old);
+fail:
+ os_info_init = 1;
+ os_info_old = NULL;
+}
+
+/*
+ * Return pointer to os infor entry and its size
+ */
+void *os_info_old_entry(int nr, unsigned long *size)
+{
+ os_info_old_init();
+
+ if (!os_info_old)
+ return NULL;
+ if (!os_info_old->entry[nr].addr)
+ return NULL;
+ *size = (unsigned long) os_info_old->entry[nr].size;
+ return (void *)(unsigned long)os_info_old->entry[nr].addr;
+}
+#endif
diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c
new file mode 100644
index 00000000..cb019f42
--- /dev/null
+++ b/arch/s390/kernel/perf_cpum_cf.c
@@ -0,0 +1,690 @@
+/*
+ * Performance event support for s390x - CPU-measurement Counter Facility
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ */
+#define KMSG_COMPONENT "cpum_cf"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/perf_event.h>
+#include <linux/percpu.h>
+#include <linux/notifier.h>
+#include <linux/init.h>
+#include <linux/export.h>
+#include <asm/ctl_reg.h>
+#include <asm/irq.h>
+#include <asm/cpu_mf.h>
+
+/* CPU-measurement counter facility supports these CPU counter sets:
+ * For CPU counter sets:
+ * Basic counter set: 0-31
+ * Problem-state counter set: 32-63
+ * Crypto-activity counter set: 64-127
+ * Extented counter set: 128-159
+ */
+enum cpumf_ctr_set {
+ /* CPU counter sets */
+ CPUMF_CTR_SET_BASIC = 0,
+ CPUMF_CTR_SET_USER = 1,
+ CPUMF_CTR_SET_CRYPTO = 2,
+ CPUMF_CTR_SET_EXT = 3,
+
+ /* Maximum number of counter sets */
+ CPUMF_CTR_SET_MAX,
+};
+
+#define CPUMF_LCCTL_ENABLE_SHIFT 16
+#define CPUMF_LCCTL_ACTCTL_SHIFT 0
+static const u64 cpumf_state_ctl[CPUMF_CTR_SET_MAX] = {
+ [CPUMF_CTR_SET_BASIC] = 0x02,
+ [CPUMF_CTR_SET_USER] = 0x04,
+ [CPUMF_CTR_SET_CRYPTO] = 0x08,
+ [CPUMF_CTR_SET_EXT] = 0x01,
+};
+
+static void ctr_set_enable(u64 *state, int ctr_set)
+{
+ *state |= cpumf_state_ctl[ctr_set] << CPUMF_LCCTL_ENABLE_SHIFT;
+}
+static void ctr_set_disable(u64 *state, int ctr_set)
+{
+ *state &= ~(cpumf_state_ctl[ctr_set] << CPUMF_LCCTL_ENABLE_SHIFT);
+}
+static void ctr_set_start(u64 *state, int ctr_set)
+{
+ *state |= cpumf_state_ctl[ctr_set] << CPUMF_LCCTL_ACTCTL_SHIFT;
+}
+static void ctr_set_stop(u64 *state, int ctr_set)
+{
+ *state &= ~(cpumf_state_ctl[ctr_set] << CPUMF_LCCTL_ACTCTL_SHIFT);
+}
+
+/* Local CPUMF event structure */
+struct cpu_hw_events {
+ struct cpumf_ctr_info info;
+ atomic_t ctr_set[CPUMF_CTR_SET_MAX];
+ u64 state, tx_state;
+ unsigned int flags;
+};
+static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = {
+ .ctr_set = {
+ [CPUMF_CTR_SET_BASIC] = ATOMIC_INIT(0),
+ [CPUMF_CTR_SET_USER] = ATOMIC_INIT(0),
+ [CPUMF_CTR_SET_CRYPTO] = ATOMIC_INIT(0),
+ [CPUMF_CTR_SET_EXT] = ATOMIC_INIT(0),
+ },
+ .state = 0,
+ .flags = 0,
+};
+
+static int get_counter_set(u64 event)
+{
+ int set = -1;
+
+ if (event < 32)
+ set = CPUMF_CTR_SET_BASIC;
+ else if (event < 64)
+ set = CPUMF_CTR_SET_USER;
+ else if (event < 128)
+ set = CPUMF_CTR_SET_CRYPTO;
+ else if (event < 160)
+ set = CPUMF_CTR_SET_EXT;
+
+ return set;
+}
+
+static int validate_event(const struct hw_perf_event *hwc)
+{
+ switch (hwc->config_base) {
+ case CPUMF_CTR_SET_BASIC:
+ case CPUMF_CTR_SET_USER:
+ case CPUMF_CTR_SET_CRYPTO:
+ case CPUMF_CTR_SET_EXT:
+ /* check for reserved counters */
+ if ((hwc->config >= 6 && hwc->config <= 31) ||
+ (hwc->config >= 38 && hwc->config <= 63) ||
+ (hwc->config >= 80 && hwc->config <= 127))
+ return -EOPNOTSUPP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int validate_ctr_version(const struct hw_perf_event *hwc)
+{
+ struct cpu_hw_events *cpuhw;
+ int err = 0;
+
+ cpuhw = &get_cpu_var(cpu_hw_events);
+
+ /* check required version for counter sets */
+ switch (hwc->config_base) {
+ case CPUMF_CTR_SET_BASIC:
+ case CPUMF_CTR_SET_USER:
+ if (cpuhw->info.cfvn < 1)
+ err = -EOPNOTSUPP;
+ break;
+ case CPUMF_CTR_SET_CRYPTO:
+ case CPUMF_CTR_SET_EXT:
+ if (cpuhw->info.csvn < 1)
+ err = -EOPNOTSUPP;
+ break;
+ }
+
+ put_cpu_var(cpu_hw_events);
+ return err;
+}
+
+static int validate_ctr_auth(const struct hw_perf_event *hwc)
+{
+ struct cpu_hw_events *cpuhw;
+ u64 ctrs_state;
+ int err = 0;
+
+ cpuhw = &get_cpu_var(cpu_hw_events);
+
+ /* check authorization for cpu counter sets */
+ ctrs_state = cpumf_state_ctl[hwc->config_base];
+ if (!(ctrs_state & cpuhw->info.auth_ctl))
+ err = -EPERM;
+
+ put_cpu_var(cpu_hw_events);
+ return err;
+}
+
+/*
+ * Change the CPUMF state to active.
+ * Enable and activate the CPU-counter sets according
+ * to the per-cpu control state.
+ */
+static void cpumf_pmu_enable(struct pmu *pmu)
+{
+ struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+ int err;
+
+ if (cpuhw->flags & PMU_F_ENABLED)
+ return;
+
+ err = lcctl(cpuhw->state);
+ if (err) {
+ pr_err("Enabling the performance measuring unit "
+ "failed with rc=%x\n", err);
+ return;
+ }
+
+ cpuhw->flags |= PMU_F_ENABLED;
+}
+
+/*
+ * Change the CPUMF state to inactive.
+ * Disable and enable (inactive) the CPU-counter sets according
+ * to the per-cpu control state.
+ */
+static void cpumf_pmu_disable(struct pmu *pmu)
+{
+ struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+ int err;
+ u64 inactive;
+
+ if (!(cpuhw->flags & PMU_F_ENABLED))
+ return;
+
+ inactive = cpuhw->state & ~((1 << CPUMF_LCCTL_ENABLE_SHIFT) - 1);
+ err = lcctl(inactive);
+ if (err) {
+ pr_err("Disabling the performance measuring unit "
+ "failed with rc=%x\n", err);
+ return;
+ }
+
+ cpuhw->flags &= ~PMU_F_ENABLED;
+}
+
+
+/* Number of perf events counting hardware events */
+static atomic_t num_events = ATOMIC_INIT(0);
+/* Used to avoid races in calling reserve/release_cpumf_hardware */
+static DEFINE_MUTEX(pmc_reserve_mutex);
+
+/* CPU-measurement alerts for the counter facility */
+static void cpumf_measurement_alert(struct ext_code ext_code,
+ unsigned int alert, unsigned long unused)
+{
+ struct cpu_hw_events *cpuhw;
+
+ if (!(alert & CPU_MF_INT_CF_MASK))
+ return;
+
+ kstat_cpu(smp_processor_id()).irqs[EXTINT_CPM]++;
+ cpuhw = &__get_cpu_var(cpu_hw_events);
+
+ /* Measurement alerts are shared and might happen when the PMU
+ * is not reserved. Ignore these alerts in this case. */
+ if (!(cpuhw->flags & PMU_F_RESERVED))
+ return;
+
+ /* counter authorization change alert */
+ if (alert & CPU_MF_INT_CF_CACA)
+ qctri(&cpuhw->info);
+
+ /* loss of counter data alert */
+ if (alert & CPU_MF_INT_CF_LCDA)
+ pr_err("CPU[%i] Counter data was lost\n", smp_processor_id());
+}
+
+#define PMC_INIT 0
+#define PMC_RELEASE 1
+static void setup_pmc_cpu(void *flags)
+{
+ struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+
+ switch (*((int *) flags)) {
+ case PMC_INIT:
+ memset(&cpuhw->info, 0, sizeof(cpuhw->info));
+ qctri(&cpuhw->info);
+ cpuhw->flags |= PMU_F_RESERVED;
+ break;
+
+ case PMC_RELEASE:
+ cpuhw->flags &= ~PMU_F_RESERVED;
+ break;
+ }
+
+ /* Disable CPU counter sets */
+ lcctl(0);
+}
+
+/* Initialize the CPU-measurement facility */
+static int reserve_pmc_hardware(void)
+{
+ int flags = PMC_INIT;
+
+ on_each_cpu(setup_pmc_cpu, &flags, 1);
+ measurement_alert_subclass_register();
+
+ return 0;
+}
+
+/* Release the CPU-measurement facility */
+static void release_pmc_hardware(void)
+{
+ int flags = PMC_RELEASE;
+
+ on_each_cpu(setup_pmc_cpu, &flags, 1);
+ measurement_alert_subclass_unregister();
+}
+
+/* Release the PMU if event is the last perf event */
+static void hw_perf_event_destroy(struct perf_event *event)
+{
+ if (!atomic_add_unless(&num_events, -1, 1)) {
+ mutex_lock(&pmc_reserve_mutex);
+ if (atomic_dec_return(&num_events) == 0)
+ release_pmc_hardware();
+ mutex_unlock(&pmc_reserve_mutex);
+ }
+}
+
+/* CPUMF <-> perf event mappings for kernel+userspace (basic set) */
+static const int cpumf_generic_events_basic[] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = 0,
+ [PERF_COUNT_HW_INSTRUCTIONS] = 1,
+ [PERF_COUNT_HW_CACHE_REFERENCES] = -1,
+ [PERF_COUNT_HW_CACHE_MISSES] = -1,
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = -1,
+ [PERF_COUNT_HW_BRANCH_MISSES] = -1,
+ [PERF_COUNT_HW_BUS_CYCLES] = -1,
+};
+/* CPUMF <-> perf event mappings for userspace (problem-state set) */
+static const int cpumf_generic_events_user[] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = 32,
+ [PERF_COUNT_HW_INSTRUCTIONS] = 33,
+ [PERF_COUNT_HW_CACHE_REFERENCES] = -1,
+ [PERF_COUNT_HW_CACHE_MISSES] = -1,
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = -1,
+ [PERF_COUNT_HW_BRANCH_MISSES] = -1,
+ [PERF_COUNT_HW_BUS_CYCLES] = -1,
+};
+
+static int __hw_perf_event_init(struct perf_event *event)
+{
+ struct perf_event_attr *attr = &event->attr;
+ struct hw_perf_event *hwc = &event->hw;
+ int err;
+ u64 ev;
+
+ switch (attr->type) {
+ case PERF_TYPE_RAW:
+ /* Raw events are used to access counters directly,
+ * hence do not permit excludes */
+ if (attr->exclude_kernel || attr->exclude_user ||
+ attr->exclude_hv)
+ return -EOPNOTSUPP;
+ ev = attr->config;
+ break;
+
+ case PERF_TYPE_HARDWARE:
+ ev = attr->config;
+ /* Count user space (problem-state) only */
+ if (!attr->exclude_user && attr->exclude_kernel) {
+ if (ev >= ARRAY_SIZE(cpumf_generic_events_user))
+ return -EOPNOTSUPP;
+ ev = cpumf_generic_events_user[ev];
+
+ /* No support for kernel space counters only */
+ } else if (!attr->exclude_kernel && attr->exclude_user) {
+ return -EOPNOTSUPP;
+
+ /* Count user and kernel space */
+ } else {
+ if (ev >= ARRAY_SIZE(cpumf_generic_events_basic))
+ return -EOPNOTSUPP;
+ ev = cpumf_generic_events_basic[ev];
+ }
+ break;
+
+ default:
+ return -ENOENT;
+ }
+
+ if (ev == -1)
+ return -ENOENT;
+
+ if (ev >= PERF_CPUM_CF_MAX_CTR)
+ return -EINVAL;
+
+ /* The CPU measurement counter facility does not have any interrupts
+ * to do sampling. Sampling must be provided by external means,
+ * for example, by timers.
+ */
+ if (hwc->sample_period)
+ return -EINVAL;
+
+ /* Use the hardware perf event structure to store the counter number
+ * in 'config' member and the counter set to which the counter belongs
+ * in the 'config_base'. The counter set (config_base) is then used
+ * to enable/disable the counters.
+ */
+ hwc->config = ev;
+ hwc->config_base = get_counter_set(ev);
+
+ /* Validate the counter that is assigned to this event.
+ * Because the counter facility can use numerous counters at the
+ * same time without constraints, it is not necessary to explicity
+ * validate event groups (event->group_leader != event).
+ */
+ err = validate_event(hwc);
+ if (err)
+ return err;
+
+ /* Initialize for using the CPU-measurement counter facility */
+ if (!atomic_inc_not_zero(&num_events)) {
+ mutex_lock(&pmc_reserve_mutex);
+ if (atomic_read(&num_events) == 0 && reserve_pmc_hardware())
+ err = -EBUSY;
+ else
+ atomic_inc(&num_events);
+ mutex_unlock(&pmc_reserve_mutex);
+ }
+ event->destroy = hw_perf_event_destroy;
+
+ /* Finally, validate version and authorization of the counter set */
+ err = validate_ctr_auth(hwc);
+ if (!err)
+ err = validate_ctr_version(hwc);
+
+ return err;
+}
+
+static int cpumf_pmu_event_init(struct perf_event *event)
+{
+ int err;
+
+ switch (event->attr.type) {
+ case PERF_TYPE_HARDWARE:
+ case PERF_TYPE_HW_CACHE:
+ case PERF_TYPE_RAW:
+ err = __hw_perf_event_init(event);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ if (unlikely(err) && event->destroy)
+ event->destroy(event);
+
+ return err;
+}
+
+static int hw_perf_event_reset(struct perf_event *event)
+{
+ u64 prev, new;
+ int err;
+
+ do {
+ prev = local64_read(&event->hw.prev_count);
+ err = ecctr(event->hw.config, &new);
+ if (err) {
+ if (err != 3)
+ break;
+ /* The counter is not (yet) available. This
+ * might happen if the counter set to which
+ * this counter belongs is in the disabled
+ * state.
+ */
+ new = 0;
+ }
+ } while (local64_cmpxchg(&event->hw.prev_count, prev, new) != prev);
+
+ return err;
+}
+
+static int hw_perf_event_update(struct perf_event *event)
+{
+ u64 prev, new, delta;
+ int err;
+
+ do {
+ prev = local64_read(&event->hw.prev_count);
+ err = ecctr(event->hw.config, &new);
+ if (err)
+ goto out;
+ } while (local64_cmpxchg(&event->hw.prev_count, prev, new) != prev);
+
+ delta = (prev <= new) ? new - prev
+ : (-1ULL - prev) + new + 1; /* overflow */
+ local64_add(delta, &event->count);
+out:
+ return err;
+}
+
+static void cpumf_pmu_read(struct perf_event *event)
+{
+ if (event->hw.state & PERF_HES_STOPPED)
+ return;
+
+ hw_perf_event_update(event);
+}
+
+static void cpumf_pmu_start(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+
+ if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
+ return;
+
+ if (WARN_ON_ONCE(hwc->config == -1))
+ return;
+
+ if (flags & PERF_EF_RELOAD)
+ WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+
+ hwc->state = 0;
+
+ /* (Re-)enable and activate the counter set */
+ ctr_set_enable(&cpuhw->state, hwc->config_base);
+ ctr_set_start(&cpuhw->state, hwc->config_base);
+
+ /* The counter set to which this counter belongs can be already active.
+ * Because all counters in a set are active, the event->hw.prev_count
+ * needs to be synchronized. At this point, the counter set can be in
+ * the inactive or disabled state.
+ */
+ hw_perf_event_reset(event);
+
+ /* increment refcount for this counter set */
+ atomic_inc(&cpuhw->ctr_set[hwc->config_base]);
+}
+
+static void cpumf_pmu_stop(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+ struct hw_perf_event *hwc = &event->hw;
+
+ if (!(hwc->state & PERF_HES_STOPPED)) {
+ /* Decrement reference count for this counter set and if this
+ * is the last used counter in the set, clear activation
+ * control and set the counter set state to inactive.
+ */
+ if (!atomic_dec_return(&cpuhw->ctr_set[hwc->config_base]))
+ ctr_set_stop(&cpuhw->state, hwc->config_base);
+ event->hw.state |= PERF_HES_STOPPED;
+ }
+
+ if ((flags & PERF_EF_UPDATE) && !(hwc->state & PERF_HES_UPTODATE)) {
+ hw_perf_event_update(event);
+ event->hw.state |= PERF_HES_UPTODATE;
+ }
+}
+
+static int cpumf_pmu_add(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+
+ /* Check authorization for the counter set to which this
+ * counter belongs.
+ * For group events transaction, the authorization check is
+ * done in cpumf_pmu_commit_txn().
+ */
+ if (!(cpuhw->flags & PERF_EVENT_TXN))
+ if (validate_ctr_auth(&event->hw))
+ return -EPERM;
+
+ ctr_set_enable(&cpuhw->state, event->hw.config_base);
+ event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED;
+
+ if (flags & PERF_EF_START)
+ cpumf_pmu_start(event, PERF_EF_RELOAD);
+
+ perf_event_update_userpage(event);
+
+ return 0;
+}
+
+static void cpumf_pmu_del(struct perf_event *event, int flags)
+{
+ struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+
+ cpumf_pmu_stop(event, PERF_EF_UPDATE);
+
+ /* Check if any counter in the counter set is still used. If not used,
+ * change the counter set to the disabled state. This also clears the
+ * content of all counters in the set.
+ *
+ * When a new perf event has been added but not yet started, this can
+ * clear enable control and resets all counters in a set. Therefore,
+ * cpumf_pmu_start() always has to reenable a counter set.
+ */
+ if (!atomic_read(&cpuhw->ctr_set[event->hw.config_base]))
+ ctr_set_disable(&cpuhw->state, event->hw.config_base);
+
+ perf_event_update_userpage(event);
+}
+
+/*
+ * Start group events scheduling transaction.
+ * Set flags to perform a single test at commit time.
+ */
+static void cpumf_pmu_start_txn(struct pmu *pmu)
+{
+ struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+
+ perf_pmu_disable(pmu);
+ cpuhw->flags |= PERF_EVENT_TXN;
+ cpuhw->tx_state = cpuhw->state;
+}
+
+/*
+ * Stop and cancel a group events scheduling tranctions.
+ * Assumes cpumf_pmu_del() is called for each successful added
+ * cpumf_pmu_add() during the transaction.
+ */
+static void cpumf_pmu_cancel_txn(struct pmu *pmu)
+{
+ struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+
+ WARN_ON(cpuhw->tx_state != cpuhw->state);
+
+ cpuhw->flags &= ~PERF_EVENT_TXN;
+ perf_pmu_enable(pmu);
+}
+
+/*
+ * Commit the group events scheduling transaction. On success, the
+ * transaction is closed. On error, the transaction is kept open
+ * until cpumf_pmu_cancel_txn() is called.
+ */
+static int cpumf_pmu_commit_txn(struct pmu *pmu)
+{
+ struct cpu_hw_events *cpuhw = &__get_cpu_var(cpu_hw_events);
+ u64 state;
+
+ /* check if the updated state can be scheduled */
+ state = cpuhw->state & ~((1 << CPUMF_LCCTL_ENABLE_SHIFT) - 1);
+ state >>= CPUMF_LCCTL_ENABLE_SHIFT;
+ if ((state & cpuhw->info.auth_ctl) != state)
+ return -EPERM;
+
+ cpuhw->flags &= ~PERF_EVENT_TXN;
+ perf_pmu_enable(pmu);
+ return 0;
+}
+
+/* Performance monitoring unit for s390x */
+static struct pmu cpumf_pmu = {
+ .pmu_enable = cpumf_pmu_enable,
+ .pmu_disable = cpumf_pmu_disable,
+ .event_init = cpumf_pmu_event_init,
+ .add = cpumf_pmu_add,
+ .del = cpumf_pmu_del,
+ .start = cpumf_pmu_start,
+ .stop = cpumf_pmu_stop,
+ .read = cpumf_pmu_read,
+ .start_txn = cpumf_pmu_start_txn,
+ .commit_txn = cpumf_pmu_commit_txn,
+ .cancel_txn = cpumf_pmu_cancel_txn,
+};
+
+static int __cpuinit cpumf_pmu_notifier(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (long) hcpu;
+ int flags;
+
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_ONLINE:
+ flags = PMC_INIT;
+ smp_call_function_single(cpu, setup_pmc_cpu, &flags, 1);
+ break;
+ case CPU_DOWN_PREPARE:
+ flags = PMC_RELEASE;
+ smp_call_function_single(cpu, setup_pmc_cpu, &flags, 1);
+ break;
+ default:
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static int __init cpumf_pmu_init(void)
+{
+ int rc;
+
+ if (!cpum_cf_avail())
+ return -ENODEV;
+
+ /* clear bit 15 of cr0 to unauthorize problem-state to
+ * extract measurement counters */
+ ctl_clear_bit(0, 48);
+
+ /* register handler for measurement-alert interruptions */
+ rc = register_external_interrupt(0x1407, cpumf_measurement_alert);
+ if (rc) {
+ pr_err("Registering for CPU-measurement alerts "
+ "failed with rc=%i\n", rc);
+ goto out;
+ }
+
+ rc = perf_pmu_register(&cpumf_pmu, "cpum_cf", PERF_TYPE_RAW);
+ if (rc) {
+ pr_err("Registering the cpum_cf PMU failed with rc=%i\n", rc);
+ unregister_external_interrupt(0x1407, cpumf_measurement_alert);
+ goto out;
+ }
+ perf_cpu_notifier(cpumf_pmu_notifier);
+out:
+ return rc;
+}
+early_initcall(cpumf_pmu_init);
diff --git a/arch/s390/kernel/perf_event.c b/arch/s390/kernel/perf_event.c
new file mode 100644
index 00000000..f58f37f6
--- /dev/null
+++ b/arch/s390/kernel/perf_event.c
@@ -0,0 +1,124 @@
+/*
+ * Performance event support for s390x
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ */
+#define KMSG_COMPONENT "perf"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/perf_event.h>
+#include <linux/percpu.h>
+#include <linux/export.h>
+#include <asm/irq.h>
+#include <asm/cpu_mf.h>
+#include <asm/lowcore.h>
+#include <asm/processor.h>
+
+const char *perf_pmu_name(void)
+{
+ if (cpum_cf_avail() || cpum_sf_avail())
+ return "CPU-measurement facilities (CPUMF)";
+ return "pmu";
+}
+EXPORT_SYMBOL(perf_pmu_name);
+
+int perf_num_counters(void)
+{
+ int num = 0;
+
+ if (cpum_cf_avail())
+ num += PERF_CPUM_CF_MAX_CTR;
+
+ return num;
+}
+EXPORT_SYMBOL(perf_num_counters);
+
+void perf_event_print_debug(void)
+{
+ struct cpumf_ctr_info cf_info;
+ unsigned long flags;
+ int cpu;
+
+ if (!cpum_cf_avail())
+ return;
+
+ local_irq_save(flags);
+
+ cpu = smp_processor_id();
+ memset(&cf_info, 0, sizeof(cf_info));
+ if (!qctri(&cf_info)) {
+ pr_info("CPU[%i] CPUM_CF: ver=%u.%u A=%04x E=%04x C=%04x\n",
+ cpu, cf_info.cfvn, cf_info.csvn,
+ cf_info.auth_ctl, cf_info.enable_ctl, cf_info.act_ctl);
+ print_hex_dump_bytes("CPUMF Query: ", DUMP_PREFIX_OFFSET,
+ &cf_info, sizeof(cf_info));
+ }
+
+ local_irq_restore(flags);
+}
+
+/* See also arch/s390/kernel/traps.c */
+static unsigned long __store_trace(struct perf_callchain_entry *entry,
+ unsigned long sp,
+ unsigned long low, unsigned long high)
+{
+ struct stack_frame *sf;
+ struct pt_regs *regs;
+
+ while (1) {
+ sp = sp & PSW_ADDR_INSN;
+ if (sp < low || sp > high - sizeof(*sf))
+ return sp;
+ sf = (struct stack_frame *) sp;
+ perf_callchain_store(entry, sf->gprs[8] & PSW_ADDR_INSN);
+ /* Follow the backchain. */
+ while (1) {
+ low = sp;
+ sp = sf->back_chain & PSW_ADDR_INSN;
+ if (!sp)
+ break;
+ if (sp <= low || sp > high - sizeof(*sf))
+ return sp;
+ sf = (struct stack_frame *) sp;
+ perf_callchain_store(entry,
+ sf->gprs[8] & PSW_ADDR_INSN);
+ }
+ /* Zero backchain detected, check for interrupt frame. */
+ sp = (unsigned long) (sf + 1);
+ if (sp <= low || sp > high - sizeof(*regs))
+ return sp;
+ regs = (struct pt_regs *) sp;
+ perf_callchain_store(entry, sf->gprs[8] & PSW_ADDR_INSN);
+ low = sp;
+ sp = regs->gprs[15];
+ }
+}
+
+void perf_callchain_kernel(struct perf_callchain_entry *entry,
+ struct pt_regs *regs)
+{
+ unsigned long head;
+ struct stack_frame *head_sf;
+
+ if (user_mode(regs))
+ return;
+
+ head = regs->gprs[15];
+ head_sf = (struct stack_frame *) head;
+
+ if (!head_sf || !head_sf->back_chain)
+ return;
+
+ head = head_sf->back_chain;
+ head = __store_trace(entry, head, S390_lowcore.async_stack - ASYNC_SIZE,
+ S390_lowcore.async_stack);
+
+ __store_trace(entry, head, S390_lowcore.thread_info,
+ S390_lowcore.thread_info + THREAD_SIZE);
+}
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
new file mode 100644
index 00000000..60055cef
--- /dev/null
+++ b/arch/s390/kernel/process.c
@@ -0,0 +1,354 @@
+/*
+ * This file handles the architecture dependent parts of process handling.
+ *
+ * Copyright IBM Corp. 1999,2009
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>,
+ * Hartmut Penner <hp@de.ibm.com>,
+ * Denis Joseph Barrow,
+ */
+
+#include <linux/compiler.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/elfcore.h>
+#include <linux/smp.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/tick.h>
+#include <linux/personality.h>
+#include <linux/syscalls.h>
+#include <linux/compat.h>
+#include <linux/kprobes.h>
+#include <linux/random.h>
+#include <linux/module.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/irq.h>
+#include <asm/timer.h>
+#include <asm/nmi.h>
+#include <asm/smp.h>
+#include <asm/switch_to.h>
+#include "entry.h"
+
+asmlinkage void ret_from_fork(void) asm ("ret_from_fork");
+
+/*
+ * Return saved PC of a blocked thread. used in kernel/sched.
+ * resume in entry.S does not create a new stack frame, it
+ * just stores the registers %r6-%r15 to the frame given by
+ * schedule. We want to return the address of the caller of
+ * schedule, so we have to walk the backchain one time to
+ * find the frame schedule() store its return address.
+ */
+unsigned long thread_saved_pc(struct task_struct *tsk)
+{
+ struct stack_frame *sf, *low, *high;
+
+ if (!tsk || !task_stack_page(tsk))
+ return 0;
+ low = task_stack_page(tsk);
+ high = (struct stack_frame *) task_pt_regs(tsk);
+ sf = (struct stack_frame *) (tsk->thread.ksp & PSW_ADDR_INSN);
+ if (sf <= low || sf > high)
+ return 0;
+ sf = (struct stack_frame *) (sf->back_chain & PSW_ADDR_INSN);
+ if (sf <= low || sf > high)
+ return 0;
+ return sf->gprs[8];
+}
+
+/*
+ * The idle loop on a S390...
+ */
+static void default_idle(void)
+{
+ if (cpu_is_offline(smp_processor_id()))
+ cpu_die();
+ local_irq_disable();
+ if (need_resched()) {
+ local_irq_enable();
+ return;
+ }
+ local_mcck_disable();
+ if (test_thread_flag(TIF_MCCK_PENDING)) {
+ local_mcck_enable();
+ local_irq_enable();
+ return;
+ }
+ /* Halt the cpu and keep track of cpu time accounting. */
+ vtime_stop_cpu();
+}
+
+void cpu_idle(void)
+{
+ for (;;) {
+ tick_nohz_idle_enter();
+ rcu_idle_enter();
+ while (!need_resched() && !test_thread_flag(TIF_MCCK_PENDING))
+ default_idle();
+ rcu_idle_exit();
+ tick_nohz_idle_exit();
+ if (test_thread_flag(TIF_MCCK_PENDING))
+ s390_handle_mcck();
+ schedule_preempt_disabled();
+ }
+}
+
+extern void __kprobes kernel_thread_starter(void);
+
+asm(
+ ".section .kprobes.text, \"ax\"\n"
+ ".global kernel_thread_starter\n"
+ "kernel_thread_starter:\n"
+ " la 2,0(10)\n"
+ " basr 14,9\n"
+ " la 2,0\n"
+ " br 11\n"
+ ".previous\n");
+
+int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+ struct pt_regs regs;
+
+ memset(&regs, 0, sizeof(regs));
+ regs.psw.mask = psw_kernel_bits |
+ PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
+ regs.psw.addr = (unsigned long) kernel_thread_starter | PSW_ADDR_AMODE;
+ regs.gprs[9] = (unsigned long) fn;
+ regs.gprs[10] = (unsigned long) arg;
+ regs.gprs[11] = (unsigned long) do_exit;
+ regs.orig_gpr2 = -1;
+
+ /* Ok, create the new process.. */
+ return do_fork(flags | CLONE_VM | CLONE_UNTRACED,
+ 0, &regs, 0, NULL, NULL);
+}
+EXPORT_SYMBOL(kernel_thread);
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+}
+
+void flush_thread(void)
+{
+}
+
+void release_thread(struct task_struct *dead_task)
+{
+}
+
+int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
+ unsigned long unused,
+ struct task_struct *p, struct pt_regs *regs)
+{
+ struct thread_info *ti;
+ struct fake_frame
+ {
+ struct stack_frame sf;
+ struct pt_regs childregs;
+ } *frame;
+
+ frame = container_of(task_pt_regs(p), struct fake_frame, childregs);
+ p->thread.ksp = (unsigned long) frame;
+ /* Store access registers to kernel stack of new process. */
+ frame->childregs = *regs;
+ frame->childregs.gprs[2] = 0; /* child returns 0 on fork. */
+ frame->childregs.gprs[15] = new_stackp;
+ frame->sf.back_chain = 0;
+
+ /* new return point is ret_from_fork */
+ frame->sf.gprs[8] = (unsigned long) ret_from_fork;
+
+ /* fake return stack for resume(), don't go back to schedule */
+ frame->sf.gprs[9] = (unsigned long) frame;
+
+ /* Save access registers to new thread structure. */
+ save_access_regs(&p->thread.acrs[0]);
+
+#ifndef CONFIG_64BIT
+ /*
+ * save fprs to current->thread.fp_regs to merge them with
+ * the emulated registers and then copy the result to the child.
+ */
+ save_fp_regs(&current->thread.fp_regs);
+ memcpy(&p->thread.fp_regs, &current->thread.fp_regs,
+ sizeof(s390_fp_regs));
+ /* Set a new TLS ? */
+ if (clone_flags & CLONE_SETTLS)
+ p->thread.acrs[0] = regs->gprs[6];
+#else /* CONFIG_64BIT */
+ /* Save the fpu registers to new thread structure. */
+ save_fp_regs(&p->thread.fp_regs);
+ /* Set a new TLS ? */
+ if (clone_flags & CLONE_SETTLS) {
+ if (is_compat_task()) {
+ p->thread.acrs[0] = (unsigned int) regs->gprs[6];
+ } else {
+ p->thread.acrs[0] = (unsigned int)(regs->gprs[6] >> 32);
+ p->thread.acrs[1] = (unsigned int) regs->gprs[6];
+ }
+ }
+#endif /* CONFIG_64BIT */
+ /* start new process with ar4 pointing to the correct address space */
+ p->thread.mm_segment = get_fs();
+ /* Don't copy debug registers */
+ memset(&p->thread.per_user, 0, sizeof(p->thread.per_user));
+ memset(&p->thread.per_event, 0, sizeof(p->thread.per_event));
+ clear_tsk_thread_flag(p, TIF_SINGLE_STEP);
+ clear_tsk_thread_flag(p, TIF_PER_TRAP);
+ /* Initialize per thread user and system timer values */
+ ti = task_thread_info(p);
+ ti->user_timer = 0;
+ ti->system_timer = 0;
+ return 0;
+}
+
+SYSCALL_DEFINE0(fork)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ return do_fork(SIGCHLD, regs->gprs[15], regs, 0, NULL, NULL);
+}
+
+SYSCALL_DEFINE4(clone, unsigned long, newsp, unsigned long, clone_flags,
+ int __user *, parent_tidptr, int __user *, child_tidptr)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+
+ if (!newsp)
+ newsp = regs->gprs[15];
+ return do_fork(clone_flags, newsp, regs, 0,
+ parent_tidptr, child_tidptr);
+}
+
+/*
+ * This is trivial, and on the face of it looks like it
+ * could equally well be done in user mode.
+ *
+ * Not so, for quite unobvious reasons - register pressure.
+ * In user mode vfork() cannot have a stack frame, and if
+ * done by calling the "clone()" system call directly, you
+ * do not have enough call-clobbered registers to hold all
+ * the information you need.
+ */
+SYSCALL_DEFINE0(vfork)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
+ regs->gprs[15], regs, 0, NULL, NULL);
+}
+
+asmlinkage void execve_tail(void)
+{
+ current->thread.fp_regs.fpc = 0;
+ if (MACHINE_HAS_IEEE)
+ asm volatile("sfpc %0,%0" : : "d" (0));
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+SYSCALL_DEFINE3(execve, const char __user *, name,
+ const char __user *const __user *, argv,
+ const char __user *const __user *, envp)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ char *filename;
+ long rc;
+
+ filename = getname(name);
+ rc = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ return rc;
+ rc = do_execve(filename, argv, envp, regs);
+ if (rc)
+ goto out;
+ execve_tail();
+ rc = regs->gprs[2];
+out:
+ putname(filename);
+ return rc;
+}
+
+/*
+ * fill in the FPU structure for a core dump.
+ */
+int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs)
+{
+#ifndef CONFIG_64BIT
+ /*
+ * save fprs to current->thread.fp_regs to merge them with
+ * the emulated registers and then copy the result to the dump.
+ */
+ save_fp_regs(&current->thread.fp_regs);
+ memcpy(fpregs, &current->thread.fp_regs, sizeof(s390_fp_regs));
+#else /* CONFIG_64BIT */
+ save_fp_regs(fpregs);
+#endif /* CONFIG_64BIT */
+ return 1;
+}
+EXPORT_SYMBOL(dump_fpu);
+
+unsigned long get_wchan(struct task_struct *p)
+{
+ struct stack_frame *sf, *low, *high;
+ unsigned long return_address;
+ int count;
+
+ if (!p || p == current || p->state == TASK_RUNNING || !task_stack_page(p))
+ return 0;
+ low = task_stack_page(p);
+ high = (struct stack_frame *) task_pt_regs(p);
+ sf = (struct stack_frame *) (p->thread.ksp & PSW_ADDR_INSN);
+ if (sf <= low || sf > high)
+ return 0;
+ for (count = 0; count < 16; count++) {
+ sf = (struct stack_frame *) (sf->back_chain & PSW_ADDR_INSN);
+ if (sf <= low || sf > high)
+ return 0;
+ return_address = sf->gprs[8] & PSW_ADDR_INSN;
+ if (!in_sched_functions(return_address))
+ return return_address;
+ }
+ return 0;
+}
+
+unsigned long arch_align_stack(unsigned long sp)
+{
+ if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
+ sp -= get_random_int() & ~PAGE_MASK;
+ return sp & ~0xf;
+}
+
+static inline unsigned long brk_rnd(void)
+{
+ /* 8MB for 32bit, 1GB for 64bit */
+ if (is_32bit_task())
+ return (get_random_int() & 0x7ffUL) << PAGE_SHIFT;
+ else
+ return (get_random_int() & 0x3ffffUL) << PAGE_SHIFT;
+}
+
+unsigned long arch_randomize_brk(struct mm_struct *mm)
+{
+ unsigned long ret = PAGE_ALIGN(mm->brk + brk_rnd());
+
+ if (ret < mm->brk)
+ return mm->brk;
+ return ret;
+}
+
+unsigned long randomize_et_dyn(unsigned long base)
+{
+ unsigned long ret = PAGE_ALIGN(base + brk_rnd());
+
+ if (!(current->flags & PF_RANDOMIZE))
+ return base;
+ if (ret < base)
+ return base;
+ return ret;
+}
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
new file mode 100644
index 00000000..6e0073e4
--- /dev/null
+++ b/arch/s390/kernel/processor.c
@@ -0,0 +1,96 @@
+/*
+ * arch/s390/kernel/processor.c
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ */
+
+#define KMSG_COMPONENT "cpu"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/cpu.h>
+#include <asm/elf.h>
+#include <asm/lowcore.h>
+#include <asm/param.h>
+
+static DEFINE_PER_CPU(struct cpuid, cpu_id);
+
+/*
+ * cpu_init - initializes state that is per-CPU.
+ */
+void __cpuinit cpu_init(void)
+{
+ struct cpuid *id = &per_cpu(cpu_id, smp_processor_id());
+
+ get_cpu_id(id);
+ atomic_inc(&init_mm.mm_count);
+ current->active_mm = &init_mm;
+ BUG_ON(current->mm);
+ enter_lazy_tlb(&init_mm, current);
+}
+
+/*
+ * show_cpuinfo - Get information on one CPU for use by procfs.
+ */
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+ static const char *hwcap_str[10] = {
+ "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp",
+ "edat", "etf3eh", "highgprs"
+ };
+ unsigned long n = (unsigned long) v - 1;
+ int i;
+
+ if (!n) {
+ s390_adjust_jiffies();
+ seq_printf(m, "vendor_id : IBM/S390\n"
+ "# processors : %i\n"
+ "bogomips per cpu: %lu.%02lu\n",
+ num_online_cpus(), loops_per_jiffy/(500000/HZ),
+ (loops_per_jiffy/(5000/HZ))%100);
+ seq_puts(m, "features\t: ");
+ for (i = 0; i < 10; i++)
+ if (hwcap_str[i] && (elf_hwcap & (1UL << i)))
+ seq_printf(m, "%s ", hwcap_str[i]);
+ seq_puts(m, "\n");
+ }
+ get_online_cpus();
+ if (cpu_online(n)) {
+ struct cpuid *id = &per_cpu(cpu_id, n);
+ seq_printf(m, "processor %li: "
+ "version = %02X, "
+ "identification = %06X, "
+ "machine = %04X\n",
+ n, id->version, id->ident, id->machine);
+ }
+ put_online_cpus();
+ return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return *pos < nr_cpu_ids ? (void *)((unsigned long) *pos + 1) : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return c_start(m, pos);
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+const struct seq_operations cpuinfo_op = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = show_cpuinfo,
+};
+
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
new file mode 100644
index 00000000..02f300fb
--- /dev/null
+++ b/arch/s390/kernel/ptrace.c
@@ -0,0 +1,1237 @@
+/*
+ * Ptrace user space interface.
+ *
+ * Copyright IBM Corp. 1999,2010
+ * Author(s): Denis Joseph Barrow
+ * Martin Schwidefsky (schwidefsky@de.ibm.com)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/security.h>
+#include <linux/audit.h>
+#include <linux/signal.h>
+#include <linux/elf.h>
+#include <linux/regset.h>
+#include <linux/tracehook.h>
+#include <linux/seccomp.h>
+#include <linux/compat.h>
+#include <trace/syscall.h>
+#include <asm/segment.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+#include <asm/switch_to.h>
+#include "entry.h"
+
+#ifdef CONFIG_COMPAT
+#include "compat_ptrace.h"
+#endif
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/syscalls.h>
+
+enum s390_regset {
+ REGSET_GENERAL,
+ REGSET_FP,
+ REGSET_LAST_BREAK,
+ REGSET_SYSTEM_CALL,
+ REGSET_GENERAL_EXTENDED,
+};
+
+void update_per_regs(struct task_struct *task)
+{
+ struct pt_regs *regs = task_pt_regs(task);
+ struct thread_struct *thread = &task->thread;
+ struct per_regs old, new;
+
+ /* Copy user specified PER registers */
+ new.control = thread->per_user.control;
+ new.start = thread->per_user.start;
+ new.end = thread->per_user.end;
+
+ /* merge TIF_SINGLE_STEP into user specified PER registers. */
+ if (test_tsk_thread_flag(task, TIF_SINGLE_STEP)) {
+ new.control |= PER_EVENT_IFETCH;
+ new.start = 0;
+ new.end = PSW_ADDR_INSN;
+ }
+
+ /* Take care of the PER enablement bit in the PSW. */
+ if (!(new.control & PER_EVENT_MASK)) {
+ regs->psw.mask &= ~PSW_MASK_PER;
+ return;
+ }
+ regs->psw.mask |= PSW_MASK_PER;
+ __ctl_store(old, 9, 11);
+ if (memcmp(&new, &old, sizeof(struct per_regs)) != 0)
+ __ctl_load(new, 9, 11);
+}
+
+void user_enable_single_step(struct task_struct *task)
+{
+ set_tsk_thread_flag(task, TIF_SINGLE_STEP);
+ if (task == current)
+ update_per_regs(task);
+}
+
+void user_disable_single_step(struct task_struct *task)
+{
+ clear_tsk_thread_flag(task, TIF_SINGLE_STEP);
+ if (task == current)
+ update_per_regs(task);
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Clear all debugging related fields.
+ */
+void ptrace_disable(struct task_struct *task)
+{
+ memset(&task->thread.per_user, 0, sizeof(task->thread.per_user));
+ memset(&task->thread.per_event, 0, sizeof(task->thread.per_event));
+ clear_tsk_thread_flag(task, TIF_SINGLE_STEP);
+ clear_tsk_thread_flag(task, TIF_PER_TRAP);
+}
+
+#ifndef CONFIG_64BIT
+# define __ADDR_MASK 3
+#else
+# define __ADDR_MASK 7
+#endif
+
+static inline unsigned long __peek_user_per(struct task_struct *child,
+ addr_t addr)
+{
+ struct per_struct_kernel *dummy = NULL;
+
+ if (addr == (addr_t) &dummy->cr9)
+ /* Control bits of the active per set. */
+ return test_thread_flag(TIF_SINGLE_STEP) ?
+ PER_EVENT_IFETCH : child->thread.per_user.control;
+ else if (addr == (addr_t) &dummy->cr10)
+ /* Start address of the active per set. */
+ return test_thread_flag(TIF_SINGLE_STEP) ?
+ 0 : child->thread.per_user.start;
+ else if (addr == (addr_t) &dummy->cr11)
+ /* End address of the active per set. */
+ return test_thread_flag(TIF_SINGLE_STEP) ?
+ PSW_ADDR_INSN : child->thread.per_user.end;
+ else if (addr == (addr_t) &dummy->bits)
+ /* Single-step bit. */
+ return test_thread_flag(TIF_SINGLE_STEP) ?
+ (1UL << (BITS_PER_LONG - 1)) : 0;
+ else if (addr == (addr_t) &dummy->starting_addr)
+ /* Start address of the user specified per set. */
+ return child->thread.per_user.start;
+ else if (addr == (addr_t) &dummy->ending_addr)
+ /* End address of the user specified per set. */
+ return child->thread.per_user.end;
+ else if (addr == (addr_t) &dummy->perc_atmid)
+ /* PER code, ATMID and AI of the last PER trap */
+ return (unsigned long)
+ child->thread.per_event.cause << (BITS_PER_LONG - 16);
+ else if (addr == (addr_t) &dummy->address)
+ /* Address of the last PER trap */
+ return child->thread.per_event.address;
+ else if (addr == (addr_t) &dummy->access_id)
+ /* Access id of the last PER trap */
+ return (unsigned long)
+ child->thread.per_event.paid << (BITS_PER_LONG - 8);
+ return 0;
+}
+
+/*
+ * Read the word at offset addr from the user area of a process. The
+ * trouble here is that the information is littered over different
+ * locations. The process registers are found on the kernel stack,
+ * the floating point stuff and the trace settings are stored in
+ * the task structure. In addition the different structures in
+ * struct user contain pad bytes that should be read as zeroes.
+ * Lovely...
+ */
+static unsigned long __peek_user(struct task_struct *child, addr_t addr)
+{
+ struct user *dummy = NULL;
+ addr_t offset, tmp;
+
+ if (addr < (addr_t) &dummy->regs.acrs) {
+ /*
+ * psw and gprs are stored on the stack
+ */
+ tmp = *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr);
+ if (addr == (addr_t) &dummy->regs.psw.mask)
+ /* Return a clean psw mask. */
+ tmp = psw_user_bits | (tmp & PSW_MASK_USER);
+
+ } else if (addr < (addr_t) &dummy->regs.orig_gpr2) {
+ /*
+ * access registers are stored in the thread structure
+ */
+ offset = addr - (addr_t) &dummy->regs.acrs;
+#ifdef CONFIG_64BIT
+ /*
+ * Very special case: old & broken 64 bit gdb reading
+ * from acrs[15]. Result is a 64 bit value. Read the
+ * 32 bit acrs[15] value and shift it by 32. Sick...
+ */
+ if (addr == (addr_t) &dummy->regs.acrs[15])
+ tmp = ((unsigned long) child->thread.acrs[15]) << 32;
+ else
+#endif
+ tmp = *(addr_t *)((addr_t) &child->thread.acrs + offset);
+
+ } else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
+ /*
+ * orig_gpr2 is stored on the kernel stack
+ */
+ tmp = (addr_t) task_pt_regs(child)->orig_gpr2;
+
+ } else if (addr < (addr_t) &dummy->regs.fp_regs) {
+ /*
+ * prevent reads of padding hole between
+ * orig_gpr2 and fp_regs on s390.
+ */
+ tmp = 0;
+
+ } else if (addr < (addr_t) (&dummy->regs.fp_regs + 1)) {
+ /*
+ * floating point regs. are stored in the thread structure
+ */
+ offset = addr - (addr_t) &dummy->regs.fp_regs;
+ tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset);
+ if (addr == (addr_t) &dummy->regs.fp_regs.fpc)
+ tmp &= (unsigned long) FPC_VALID_MASK
+ << (BITS_PER_LONG - 32);
+
+ } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
+ /*
+ * Handle access to the per_info structure.
+ */
+ addr -= (addr_t) &dummy->regs.per_info;
+ tmp = __peek_user_per(child, addr);
+
+ } else
+ tmp = 0;
+
+ return tmp;
+}
+
+static int
+peek_user(struct task_struct *child, addr_t addr, addr_t data)
+{
+ addr_t tmp, mask;
+
+ /*
+ * Stupid gdb peeks/pokes the access registers in 64 bit with
+ * an alignment of 4. Programmers from hell...
+ */
+ mask = __ADDR_MASK;
+#ifdef CONFIG_64BIT
+ if (addr >= (addr_t) &((struct user *) NULL)->regs.acrs &&
+ addr < (addr_t) &((struct user *) NULL)->regs.orig_gpr2)
+ mask = 3;
+#endif
+ if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
+ return -EIO;
+
+ tmp = __peek_user(child, addr);
+ return put_user(tmp, (addr_t __user *) data);
+}
+
+static inline void __poke_user_per(struct task_struct *child,
+ addr_t addr, addr_t data)
+{
+ struct per_struct_kernel *dummy = NULL;
+
+ /*
+ * There are only three fields in the per_info struct that the
+ * debugger user can write to.
+ * 1) cr9: the debugger wants to set a new PER event mask
+ * 2) starting_addr: the debugger wants to set a new starting
+ * address to use with the PER event mask.
+ * 3) ending_addr: the debugger wants to set a new ending
+ * address to use with the PER event mask.
+ * The user specified PER event mask and the start and end
+ * addresses are used only if single stepping is not in effect.
+ * Writes to any other field in per_info are ignored.
+ */
+ if (addr == (addr_t) &dummy->cr9)
+ /* PER event mask of the user specified per set. */
+ child->thread.per_user.control =
+ data & (PER_EVENT_MASK | PER_CONTROL_MASK);
+ else if (addr == (addr_t) &dummy->starting_addr)
+ /* Starting address of the user specified per set. */
+ child->thread.per_user.start = data;
+ else if (addr == (addr_t) &dummy->ending_addr)
+ /* Ending address of the user specified per set. */
+ child->thread.per_user.end = data;
+}
+
+/*
+ * Write a word to the user area of a process at location addr. This
+ * operation does have an additional problem compared to peek_user.
+ * Stores to the program status word and on the floating point
+ * control register needs to get checked for validity.
+ */
+static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
+{
+ struct user *dummy = NULL;
+ addr_t offset;
+
+ if (addr < (addr_t) &dummy->regs.acrs) {
+ /*
+ * psw and gprs are stored on the stack
+ */
+ if (addr == (addr_t) &dummy->regs.psw.mask &&
+ ((data & ~PSW_MASK_USER) != psw_user_bits ||
+ ((data & PSW_MASK_EA) && !(data & PSW_MASK_BA))))
+ /* Invalid psw mask. */
+ return -EINVAL;
+ *(addr_t *)((addr_t) &task_pt_regs(child)->psw + addr) = data;
+
+ } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
+ /*
+ * access registers are stored in the thread structure
+ */
+ offset = addr - (addr_t) &dummy->regs.acrs;
+#ifdef CONFIG_64BIT
+ /*
+ * Very special case: old & broken 64 bit gdb writing
+ * to acrs[15] with a 64 bit value. Ignore the lower
+ * half of the value and write the upper 32 bit to
+ * acrs[15]. Sick...
+ */
+ if (addr == (addr_t) &dummy->regs.acrs[15])
+ child->thread.acrs[15] = (unsigned int) (data >> 32);
+ else
+#endif
+ *(addr_t *)((addr_t) &child->thread.acrs + offset) = data;
+
+ } else if (addr == (addr_t) &dummy->regs.orig_gpr2) {
+ /*
+ * orig_gpr2 is stored on the kernel stack
+ */
+ task_pt_regs(child)->orig_gpr2 = data;
+
+ } else if (addr < (addr_t) &dummy->regs.fp_regs) {
+ /*
+ * prevent writes of padding hole between
+ * orig_gpr2 and fp_regs on s390.
+ */
+ return 0;
+
+ } else if (addr < (addr_t) (&dummy->regs.fp_regs + 1)) {
+ /*
+ * floating point regs. are stored in the thread structure
+ */
+ if (addr == (addr_t) &dummy->regs.fp_regs.fpc &&
+ (data & ~((unsigned long) FPC_VALID_MASK
+ << (BITS_PER_LONG - 32))) != 0)
+ return -EINVAL;
+ offset = addr - (addr_t) &dummy->regs.fp_regs;
+ *(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data;
+
+ } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
+ /*
+ * Handle access to the per_info structure.
+ */
+ addr -= (addr_t) &dummy->regs.per_info;
+ __poke_user_per(child, addr, data);
+
+ }
+
+ return 0;
+}
+
+static int poke_user(struct task_struct *child, addr_t addr, addr_t data)
+{
+ addr_t mask;
+
+ /*
+ * Stupid gdb peeks/pokes the access registers in 64 bit with
+ * an alignment of 4. Programmers from hell indeed...
+ */
+ mask = __ADDR_MASK;
+#ifdef CONFIG_64BIT
+ if (addr >= (addr_t) &((struct user *) NULL)->regs.acrs &&
+ addr < (addr_t) &((struct user *) NULL)->regs.orig_gpr2)
+ mask = 3;
+#endif
+ if ((addr & mask) || addr > sizeof(struct user) - __ADDR_MASK)
+ return -EIO;
+
+ return __poke_user(child, addr, data);
+}
+
+long arch_ptrace(struct task_struct *child, long request,
+ unsigned long addr, unsigned long data)
+{
+ ptrace_area parea;
+ int copied, ret;
+
+ switch (request) {
+ case PTRACE_PEEKUSR:
+ /* read the word at location addr in the USER area. */
+ return peek_user(child, addr, data);
+
+ case PTRACE_POKEUSR:
+ /* write the word at location addr in the USER area */
+ return poke_user(child, addr, data);
+
+ case PTRACE_PEEKUSR_AREA:
+ case PTRACE_POKEUSR_AREA:
+ if (copy_from_user(&parea, (void __force __user *) addr,
+ sizeof(parea)))
+ return -EFAULT;
+ addr = parea.kernel_addr;
+ data = parea.process_addr;
+ copied = 0;
+ while (copied < parea.len) {
+ if (request == PTRACE_PEEKUSR_AREA)
+ ret = peek_user(child, addr, data);
+ else {
+ addr_t utmp;
+ if (get_user(utmp,
+ (addr_t __force __user *) data))
+ return -EFAULT;
+ ret = poke_user(child, addr, utmp);
+ }
+ if (ret)
+ return ret;
+ addr += sizeof(unsigned long);
+ data += sizeof(unsigned long);
+ copied += sizeof(unsigned long);
+ }
+ return 0;
+ case PTRACE_GET_LAST_BREAK:
+ put_user(task_thread_info(child)->last_break,
+ (unsigned long __user *) data);
+ return 0;
+ default:
+ /* Removing high order bit from addr (only for 31 bit). */
+ addr &= PSW_ADDR_INSN;
+ return ptrace_request(child, request, addr, data);
+ }
+}
+
+#ifdef CONFIG_COMPAT
+/*
+ * Now the fun part starts... a 31 bit program running in the
+ * 31 bit emulation tracing another program. PTRACE_PEEKTEXT,
+ * PTRACE_PEEKDATA, PTRACE_POKETEXT and PTRACE_POKEDATA are easy
+ * to handle, the difference to the 64 bit versions of the requests
+ * is that the access is done in multiples of 4 byte instead of
+ * 8 bytes (sizeof(unsigned long) on 31/64 bit).
+ * The ugly part are PTRACE_PEEKUSR, PTRACE_PEEKUSR_AREA,
+ * PTRACE_POKEUSR and PTRACE_POKEUSR_AREA. If the traced program
+ * is a 31 bit program too, the content of struct user can be
+ * emulated. A 31 bit program peeking into the struct user of
+ * a 64 bit program is a no-no.
+ */
+
+/*
+ * Same as peek_user_per but for a 31 bit program.
+ */
+static inline __u32 __peek_user_per_compat(struct task_struct *child,
+ addr_t addr)
+{
+ struct compat_per_struct_kernel *dummy32 = NULL;
+
+ if (addr == (addr_t) &dummy32->cr9)
+ /* Control bits of the active per set. */
+ return (__u32) test_thread_flag(TIF_SINGLE_STEP) ?
+ PER_EVENT_IFETCH : child->thread.per_user.control;
+ else if (addr == (addr_t) &dummy32->cr10)
+ /* Start address of the active per set. */
+ return (__u32) test_thread_flag(TIF_SINGLE_STEP) ?
+ 0 : child->thread.per_user.start;
+ else if (addr == (addr_t) &dummy32->cr11)
+ /* End address of the active per set. */
+ return test_thread_flag(TIF_SINGLE_STEP) ?
+ PSW32_ADDR_INSN : child->thread.per_user.end;
+ else if (addr == (addr_t) &dummy32->bits)
+ /* Single-step bit. */
+ return (__u32) test_thread_flag(TIF_SINGLE_STEP) ?
+ 0x80000000 : 0;
+ else if (addr == (addr_t) &dummy32->starting_addr)
+ /* Start address of the user specified per set. */
+ return (__u32) child->thread.per_user.start;
+ else if (addr == (addr_t) &dummy32->ending_addr)
+ /* End address of the user specified per set. */
+ return (__u32) child->thread.per_user.end;
+ else if (addr == (addr_t) &dummy32->perc_atmid)
+ /* PER code, ATMID and AI of the last PER trap */
+ return (__u32) child->thread.per_event.cause << 16;
+ else if (addr == (addr_t) &dummy32->address)
+ /* Address of the last PER trap */
+ return (__u32) child->thread.per_event.address;
+ else if (addr == (addr_t) &dummy32->access_id)
+ /* Access id of the last PER trap */
+ return (__u32) child->thread.per_event.paid << 24;
+ return 0;
+}
+
+/*
+ * Same as peek_user but for a 31 bit program.
+ */
+static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
+{
+ struct compat_user *dummy32 = NULL;
+ addr_t offset;
+ __u32 tmp;
+
+ if (addr < (addr_t) &dummy32->regs.acrs) {
+ struct pt_regs *regs = task_pt_regs(child);
+ /*
+ * psw and gprs are stored on the stack
+ */
+ if (addr == (addr_t) &dummy32->regs.psw.mask) {
+ /* Fake a 31 bit psw mask. */
+ tmp = (__u32)(regs->psw.mask >> 32);
+ tmp = psw32_user_bits | (tmp & PSW32_MASK_USER);
+ } else if (addr == (addr_t) &dummy32->regs.psw.addr) {
+ /* Fake a 31 bit psw address. */
+ tmp = (__u32) regs->psw.addr |
+ (__u32)(regs->psw.mask & PSW_MASK_BA);
+ } else {
+ /* gpr 0-15 */
+ tmp = *(__u32 *)((addr_t) &regs->psw + addr*2 + 4);
+ }
+ } else if (addr < (addr_t) (&dummy32->regs.orig_gpr2)) {
+ /*
+ * access registers are stored in the thread structure
+ */
+ offset = addr - (addr_t) &dummy32->regs.acrs;
+ tmp = *(__u32*)((addr_t) &child->thread.acrs + offset);
+
+ } else if (addr == (addr_t) (&dummy32->regs.orig_gpr2)) {
+ /*
+ * orig_gpr2 is stored on the kernel stack
+ */
+ tmp = *(__u32*)((addr_t) &task_pt_regs(child)->orig_gpr2 + 4);
+
+ } else if (addr < (addr_t) &dummy32->regs.fp_regs) {
+ /*
+ * prevent reads of padding hole between
+ * orig_gpr2 and fp_regs on s390.
+ */
+ tmp = 0;
+
+ } else if (addr < (addr_t) (&dummy32->regs.fp_regs + 1)) {
+ /*
+ * floating point regs. are stored in the thread structure
+ */
+ offset = addr - (addr_t) &dummy32->regs.fp_regs;
+ tmp = *(__u32 *)((addr_t) &child->thread.fp_regs + offset);
+
+ } else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) {
+ /*
+ * Handle access to the per_info structure.
+ */
+ addr -= (addr_t) &dummy32->regs.per_info;
+ tmp = __peek_user_per_compat(child, addr);
+
+ } else
+ tmp = 0;
+
+ return tmp;
+}
+
+static int peek_user_compat(struct task_struct *child,
+ addr_t addr, addr_t data)
+{
+ __u32 tmp;
+
+ if (!is_compat_task() || (addr & 3) || addr > sizeof(struct user) - 3)
+ return -EIO;
+
+ tmp = __peek_user_compat(child, addr);
+ return put_user(tmp, (__u32 __user *) data);
+}
+
+/*
+ * Same as poke_user_per but for a 31 bit program.
+ */
+static inline void __poke_user_per_compat(struct task_struct *child,
+ addr_t addr, __u32 data)
+{
+ struct compat_per_struct_kernel *dummy32 = NULL;
+
+ if (addr == (addr_t) &dummy32->cr9)
+ /* PER event mask of the user specified per set. */
+ child->thread.per_user.control =
+ data & (PER_EVENT_MASK | PER_CONTROL_MASK);
+ else if (addr == (addr_t) &dummy32->starting_addr)
+ /* Starting address of the user specified per set. */
+ child->thread.per_user.start = data;
+ else if (addr == (addr_t) &dummy32->ending_addr)
+ /* Ending address of the user specified per set. */
+ child->thread.per_user.end = data;
+}
+
+/*
+ * Same as poke_user but for a 31 bit program.
+ */
+static int __poke_user_compat(struct task_struct *child,
+ addr_t addr, addr_t data)
+{
+ struct compat_user *dummy32 = NULL;
+ __u32 tmp = (__u32) data;
+ addr_t offset;
+
+ if (addr < (addr_t) &dummy32->regs.acrs) {
+ struct pt_regs *regs = task_pt_regs(child);
+ /*
+ * psw, gprs, acrs and orig_gpr2 are stored on the stack
+ */
+ if (addr == (addr_t) &dummy32->regs.psw.mask) {
+ /* Build a 64 bit psw mask from 31 bit mask. */
+ if ((tmp & ~PSW32_MASK_USER) != psw32_user_bits)
+ /* Invalid psw mask. */
+ return -EINVAL;
+ regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
+ (regs->psw.mask & PSW_MASK_BA) |
+ (__u64)(tmp & PSW32_MASK_USER) << 32;
+ } else if (addr == (addr_t) &dummy32->regs.psw.addr) {
+ /* Build a 64 bit psw address from 31 bit address. */
+ regs->psw.addr = (__u64) tmp & PSW32_ADDR_INSN;
+ /* Transfer 31 bit amode bit to psw mask. */
+ regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) |
+ (__u64)(tmp & PSW32_ADDR_AMODE);
+ } else {
+ /* gpr 0-15 */
+ *(__u32*)((addr_t) &regs->psw + addr*2 + 4) = tmp;
+ }
+ } else if (addr < (addr_t) (&dummy32->regs.orig_gpr2)) {
+ /*
+ * access registers are stored in the thread structure
+ */
+ offset = addr - (addr_t) &dummy32->regs.acrs;
+ *(__u32*)((addr_t) &child->thread.acrs + offset) = tmp;
+
+ } else if (addr == (addr_t) (&dummy32->regs.orig_gpr2)) {
+ /*
+ * orig_gpr2 is stored on the kernel stack
+ */
+ *(__u32*)((addr_t) &task_pt_regs(child)->orig_gpr2 + 4) = tmp;
+
+ } else if (addr < (addr_t) &dummy32->regs.fp_regs) {
+ /*
+ * prevent writess of padding hole between
+ * orig_gpr2 and fp_regs on s390.
+ */
+ return 0;
+
+ } else if (addr < (addr_t) (&dummy32->regs.fp_regs + 1)) {
+ /*
+ * floating point regs. are stored in the thread structure
+ */
+ if (addr == (addr_t) &dummy32->regs.fp_regs.fpc &&
+ (tmp & ~FPC_VALID_MASK) != 0)
+ /* Invalid floating point control. */
+ return -EINVAL;
+ offset = addr - (addr_t) &dummy32->regs.fp_regs;
+ *(__u32 *)((addr_t) &child->thread.fp_regs + offset) = tmp;
+
+ } else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) {
+ /*
+ * Handle access to the per_info structure.
+ */
+ addr -= (addr_t) &dummy32->regs.per_info;
+ __poke_user_per_compat(child, addr, data);
+ }
+
+ return 0;
+}
+
+static int poke_user_compat(struct task_struct *child,
+ addr_t addr, addr_t data)
+{
+ if (!is_compat_task() || (addr & 3) ||
+ addr > sizeof(struct compat_user) - 3)
+ return -EIO;
+
+ return __poke_user_compat(child, addr, data);
+}
+
+long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
+ compat_ulong_t caddr, compat_ulong_t cdata)
+{
+ unsigned long addr = caddr;
+ unsigned long data = cdata;
+ compat_ptrace_area parea;
+ int copied, ret;
+
+ switch (request) {
+ case PTRACE_PEEKUSR:
+ /* read the word at location addr in the USER area. */
+ return peek_user_compat(child, addr, data);
+
+ case PTRACE_POKEUSR:
+ /* write the word at location addr in the USER area */
+ return poke_user_compat(child, addr, data);
+
+ case PTRACE_PEEKUSR_AREA:
+ case PTRACE_POKEUSR_AREA:
+ if (copy_from_user(&parea, (void __force __user *) addr,
+ sizeof(parea)))
+ return -EFAULT;
+ addr = parea.kernel_addr;
+ data = parea.process_addr;
+ copied = 0;
+ while (copied < parea.len) {
+ if (request == PTRACE_PEEKUSR_AREA)
+ ret = peek_user_compat(child, addr, data);
+ else {
+ __u32 utmp;
+ if (get_user(utmp,
+ (__u32 __force __user *) data))
+ return -EFAULT;
+ ret = poke_user_compat(child, addr, utmp);
+ }
+ if (ret)
+ return ret;
+ addr += sizeof(unsigned int);
+ data += sizeof(unsigned int);
+ copied += sizeof(unsigned int);
+ }
+ return 0;
+ case PTRACE_GET_LAST_BREAK:
+ put_user(task_thread_info(child)->last_break,
+ (unsigned int __user *) data);
+ return 0;
+ }
+ return compat_ptrace_request(child, request, addr, data);
+}
+#endif
+
+asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
+{
+ long ret = 0;
+
+ /* Do the secure computing check first. */
+ secure_computing(regs->gprs[2]);
+
+ /*
+ * The sysc_tracesys code in entry.S stored the system
+ * call number to gprs[2].
+ */
+ if (test_thread_flag(TIF_SYSCALL_TRACE) &&
+ (tracehook_report_syscall_entry(regs) ||
+ regs->gprs[2] >= NR_syscalls)) {
+ /*
+ * Tracing decided this syscall should not happen or the
+ * debugger stored an invalid system call number. Skip
+ * the system call and the system call restart handling.
+ */
+ clear_thread_flag(TIF_SYSCALL);
+ ret = -1;
+ }
+
+ if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+ trace_sys_enter(regs, regs->gprs[2]);
+
+ audit_syscall_entry(is_compat_task() ?
+ AUDIT_ARCH_S390 : AUDIT_ARCH_S390X,
+ regs->gprs[2], regs->orig_gpr2,
+ regs->gprs[3], regs->gprs[4],
+ regs->gprs[5]);
+ return ret ?: regs->gprs[2];
+}
+
+asmlinkage void do_syscall_trace_exit(struct pt_regs *regs)
+{
+ audit_syscall_exit(regs);
+
+ if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+ trace_sys_exit(regs, regs->gprs[2]);
+
+ if (test_thread_flag(TIF_SYSCALL_TRACE))
+ tracehook_report_syscall_exit(regs, 0);
+}
+
+/*
+ * user_regset definitions.
+ */
+
+static int s390_regs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ if (kbuf) {
+ unsigned long *k = kbuf;
+ while (count > 0) {
+ *k++ = __peek_user(target, pos);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ unsigned long __user *u = ubuf;
+ while (count > 0) {
+ if (__put_user(__peek_user(target, pos), u++))
+ return -EFAULT;
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+ return 0;
+}
+
+static int s390_regs_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int rc = 0;
+
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ if (kbuf) {
+ const unsigned long *k = kbuf;
+ while (count > 0 && !rc) {
+ rc = __poke_user(target, pos, *k++);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ const unsigned long __user *u = ubuf;
+ while (count > 0 && !rc) {
+ unsigned long word;
+ rc = __get_user(word, u++);
+ if (rc)
+ break;
+ rc = __poke_user(target, pos, word);
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+
+ if (rc == 0 && target == current)
+ restore_access_regs(target->thread.acrs);
+
+ return rc;
+}
+
+static int s390_fpregs_get(struct task_struct *target,
+ const struct user_regset *regset, unsigned int pos,
+ unsigned int count, void *kbuf, void __user *ubuf)
+{
+ if (target == current)
+ save_fp_regs(&target->thread.fp_regs);
+
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ &target->thread.fp_regs, 0, -1);
+}
+
+static int s390_fpregs_set(struct task_struct *target,
+ const struct user_regset *regset, unsigned int pos,
+ unsigned int count, const void *kbuf,
+ const void __user *ubuf)
+{
+ int rc = 0;
+
+ if (target == current)
+ save_fp_regs(&target->thread.fp_regs);
+
+ /* If setting FPC, must validate it first. */
+ if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) {
+ u32 fpc[2] = { target->thread.fp_regs.fpc, 0 };
+ rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &fpc,
+ 0, offsetof(s390_fp_regs, fprs));
+ if (rc)
+ return rc;
+ if ((fpc[0] & ~FPC_VALID_MASK) != 0 || fpc[1] != 0)
+ return -EINVAL;
+ target->thread.fp_regs.fpc = fpc[0];
+ }
+
+ if (rc == 0 && count > 0)
+ rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ target->thread.fp_regs.fprs,
+ offsetof(s390_fp_regs, fprs), -1);
+
+ if (rc == 0 && target == current)
+ restore_fp_regs(&target->thread.fp_regs);
+
+ return rc;
+}
+
+#ifdef CONFIG_64BIT
+
+static int s390_last_break_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (count > 0) {
+ if (kbuf) {
+ unsigned long *k = kbuf;
+ *k = task_thread_info(target)->last_break;
+ } else {
+ unsigned long __user *u = ubuf;
+ if (__put_user(task_thread_info(target)->last_break, u))
+ return -EFAULT;
+ }
+ }
+ return 0;
+}
+
+static int s390_last_break_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return 0;
+}
+
+#endif
+
+static int s390_system_call_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ unsigned int *data = &task_thread_info(target)->system_call;
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
+ data, 0, sizeof(unsigned int));
+}
+
+static int s390_system_call_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ unsigned int *data = &task_thread_info(target)->system_call;
+ return user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+ data, 0, sizeof(unsigned int));
+}
+
+static const struct user_regset s390_regsets[] = {
+ [REGSET_GENERAL] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = sizeof(s390_regs) / sizeof(long),
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .get = s390_regs_get,
+ .set = s390_regs_set,
+ },
+ [REGSET_FP] = {
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(s390_fp_regs) / sizeof(long),
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .get = s390_fpregs_get,
+ .set = s390_fpregs_set,
+ },
+#ifdef CONFIG_64BIT
+ [REGSET_LAST_BREAK] = {
+ .core_note_type = NT_S390_LAST_BREAK,
+ .n = 1,
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .get = s390_last_break_get,
+ .set = s390_last_break_set,
+ },
+#endif
+ [REGSET_SYSTEM_CALL] = {
+ .core_note_type = NT_S390_SYSTEM_CALL,
+ .n = 1,
+ .size = sizeof(unsigned int),
+ .align = sizeof(unsigned int),
+ .get = s390_system_call_get,
+ .set = s390_system_call_set,
+ },
+};
+
+static const struct user_regset_view user_s390_view = {
+ .name = UTS_MACHINE,
+ .e_machine = EM_S390,
+ .regsets = s390_regsets,
+ .n = ARRAY_SIZE(s390_regsets)
+};
+
+#ifdef CONFIG_COMPAT
+static int s390_compat_regs_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ if (kbuf) {
+ compat_ulong_t *k = kbuf;
+ while (count > 0) {
+ *k++ = __peek_user_compat(target, pos);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ compat_ulong_t __user *u = ubuf;
+ while (count > 0) {
+ if (__put_user(__peek_user_compat(target, pos), u++))
+ return -EFAULT;
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+ return 0;
+}
+
+static int s390_compat_regs_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int rc = 0;
+
+ if (target == current)
+ save_access_regs(target->thread.acrs);
+
+ if (kbuf) {
+ const compat_ulong_t *k = kbuf;
+ while (count > 0 && !rc) {
+ rc = __poke_user_compat(target, pos, *k++);
+ count -= sizeof(*k);
+ pos += sizeof(*k);
+ }
+ } else {
+ const compat_ulong_t __user *u = ubuf;
+ while (count > 0 && !rc) {
+ compat_ulong_t word;
+ rc = __get_user(word, u++);
+ if (rc)
+ break;
+ rc = __poke_user_compat(target, pos, word);
+ count -= sizeof(*u);
+ pos += sizeof(*u);
+ }
+ }
+
+ if (rc == 0 && target == current)
+ restore_access_regs(target->thread.acrs);
+
+ return rc;
+}
+
+static int s390_compat_regs_high_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ compat_ulong_t *gprs_high;
+
+ gprs_high = (compat_ulong_t *)
+ &task_pt_regs(target)->gprs[pos / sizeof(compat_ulong_t)];
+ if (kbuf) {
+ compat_ulong_t *k = kbuf;
+ while (count > 0) {
+ *k++ = *gprs_high;
+ gprs_high += 2;
+ count -= sizeof(*k);
+ }
+ } else {
+ compat_ulong_t __user *u = ubuf;
+ while (count > 0) {
+ if (__put_user(*gprs_high, u++))
+ return -EFAULT;
+ gprs_high += 2;
+ count -= sizeof(*u);
+ }
+ }
+ return 0;
+}
+
+static int s390_compat_regs_high_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ compat_ulong_t *gprs_high;
+ int rc = 0;
+
+ gprs_high = (compat_ulong_t *)
+ &task_pt_regs(target)->gprs[pos / sizeof(compat_ulong_t)];
+ if (kbuf) {
+ const compat_ulong_t *k = kbuf;
+ while (count > 0) {
+ *gprs_high = *k++;
+ *gprs_high += 2;
+ count -= sizeof(*k);
+ }
+ } else {
+ const compat_ulong_t __user *u = ubuf;
+ while (count > 0 && !rc) {
+ unsigned long word;
+ rc = __get_user(word, u++);
+ if (rc)
+ break;
+ *gprs_high = word;
+ *gprs_high += 2;
+ count -= sizeof(*u);
+ }
+ }
+
+ return rc;
+}
+
+static int s390_compat_last_break_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ compat_ulong_t last_break;
+
+ if (count > 0) {
+ last_break = task_thread_info(target)->last_break;
+ if (kbuf) {
+ unsigned long *k = kbuf;
+ *k = last_break;
+ } else {
+ unsigned long __user *u = ubuf;
+ if (__put_user(last_break, u))
+ return -EFAULT;
+ }
+ }
+ return 0;
+}
+
+static int s390_compat_last_break_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return 0;
+}
+
+static const struct user_regset s390_compat_regsets[] = {
+ [REGSET_GENERAL] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = sizeof(s390_compat_regs) / sizeof(compat_long_t),
+ .size = sizeof(compat_long_t),
+ .align = sizeof(compat_long_t),
+ .get = s390_compat_regs_get,
+ .set = s390_compat_regs_set,
+ },
+ [REGSET_FP] = {
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(s390_fp_regs) / sizeof(compat_long_t),
+ .size = sizeof(compat_long_t),
+ .align = sizeof(compat_long_t),
+ .get = s390_fpregs_get,
+ .set = s390_fpregs_set,
+ },
+ [REGSET_LAST_BREAK] = {
+ .core_note_type = NT_S390_LAST_BREAK,
+ .n = 1,
+ .size = sizeof(long),
+ .align = sizeof(long),
+ .get = s390_compat_last_break_get,
+ .set = s390_compat_last_break_set,
+ },
+ [REGSET_SYSTEM_CALL] = {
+ .core_note_type = NT_S390_SYSTEM_CALL,
+ .n = 1,
+ .size = sizeof(compat_uint_t),
+ .align = sizeof(compat_uint_t),
+ .get = s390_system_call_get,
+ .set = s390_system_call_set,
+ },
+ [REGSET_GENERAL_EXTENDED] = {
+ .core_note_type = NT_S390_HIGH_GPRS,
+ .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t),
+ .size = sizeof(compat_long_t),
+ .align = sizeof(compat_long_t),
+ .get = s390_compat_regs_high_get,
+ .set = s390_compat_regs_high_set,
+ },
+};
+
+static const struct user_regset_view user_s390_compat_view = {
+ .name = "s390",
+ .e_machine = EM_S390,
+ .regsets = s390_compat_regsets,
+ .n = ARRAY_SIZE(s390_compat_regsets)
+};
+#endif
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+#ifdef CONFIG_COMPAT
+ if (test_tsk_thread_flag(task, TIF_31BIT))
+ return &user_s390_compat_view;
+#endif
+ return &user_s390_view;
+}
+
+static const char *gpr_names[NUM_GPRS] = {
+ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
+};
+
+unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset)
+{
+ if (offset >= NUM_GPRS)
+ return 0;
+ return regs->gprs[offset];
+}
+
+int regs_query_register_offset(const char *name)
+{
+ unsigned long offset;
+
+ if (!name || *name != 'r')
+ return -EINVAL;
+ if (strict_strtoul(name + 1, 10, &offset))
+ return -EINVAL;
+ if (offset >= NUM_GPRS)
+ return -EINVAL;
+ return offset;
+}
+
+const char *regs_query_register_name(unsigned int offset)
+{
+ if (offset >= NUM_GPRS)
+ return NULL;
+ return gpr_names[offset];
+}
+
+static int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
+{
+ unsigned long ksp = kernel_stack_pointer(regs);
+
+ return (addr & ~(THREAD_SIZE - 1)) == (ksp & ~(THREAD_SIZE - 1));
+}
+
+/**
+ * regs_get_kernel_stack_nth() - get Nth entry of the stack
+ * @regs:pt_regs which contains kernel stack pointer.
+ * @n:stack entry number.
+ *
+ * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
+ * is specifined by @regs. If the @n th entry is NOT in the kernel stack,
+ * this returns 0.
+ */
+unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
+{
+ unsigned long addr;
+
+ addr = kernel_stack_pointer(regs) + n * sizeof(long);
+ if (!regs_within_kernel_stack(regs, addr))
+ return 0;
+ return *(unsigned long *)addr;
+}
diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S
new file mode 100644
index 00000000..ad67c214
--- /dev/null
+++ b/arch/s390/kernel/reipl.S
@@ -0,0 +1,93 @@
+/*
+ * arch/s390/kernel/reipl.S
+ *
+ * S390 version
+ * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Holger Smolinski (Holger.Smolinski@de.ibm.com)
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+
+#
+# store_status: Empty implementation until kdump is supported on 31 bit
+#
+ENTRY(store_status)
+ br %r14
+
+#
+# do_reipl_asm
+# Parameter: r2 = schid of reipl device
+#
+ENTRY(do_reipl_asm)
+ basr %r13,0
+.Lpg0: lpsw .Lnewpsw-.Lpg0(%r13)
+.Lpg1: # do store status of all registers
+
+ stm %r0,%r15,__LC_GPREGS_SAVE_AREA
+ stctl %c0,%c15,__LC_CREGS_SAVE_AREA
+ stam %a0,%a15,__LC_AREGS_SAVE_AREA
+ l %r10,.Ldump_pfx-.Lpg0(%r13)
+ mvc __LC_PREFIX_SAVE_AREA(4),0(%r10)
+ stckc .Lclkcmp-.Lpg0(%r13)
+ mvc __LC_CLOCK_COMP_SAVE_AREA(8),.Lclkcmp-.Lpg0(%r13)
+ stpt __LC_CPU_TIMER_SAVE_AREA
+ st %r13, __LC_PSW_SAVE_AREA+4
+ lctl %c6,%c6,.Lall-.Lpg0(%r13)
+ lr %r1,%r2
+ mvc __LC_PGM_NEW_PSW(8),.Lpcnew-.Lpg0(%r13)
+ stsch .Lschib-.Lpg0(%r13)
+ oi .Lschib+5-.Lpg0(%r13),0x84
+.Lecs: xi .Lschib+27-.Lpg0(%r13),0x01
+ msch .Lschib-.Lpg0(%r13)
+ lhi %r0,5
+.Lssch: ssch .Liplorb-.Lpg0(%r13)
+ jz .L001
+ brct %r0,.Lssch
+ bas %r14,.Ldisab-.Lpg0(%r13)
+.L001: mvc __LC_IO_NEW_PSW(8),.Lionew-.Lpg0(%r13)
+.Ltpi: lpsw .Lwaitpsw-.Lpg0(%r13)
+.Lcont: c %r1,__LC_SUBCHANNEL_ID
+ jnz .Ltpi
+ clc __LC_IO_INT_PARM(4),.Liplorb-.Lpg0(%r13)
+ jnz .Ltpi
+ tsch .Liplirb-.Lpg0(%r13)
+ tm .Liplirb+9-.Lpg0(%r13),0xbf
+ jz .L002
+ bas %r14,.Ldisab-.Lpg0(%r13)
+.L002: tm .Liplirb+8-.Lpg0(%r13),0xf3
+ jz .L003
+ bas %r14,.Ldisab-.Lpg0(%r13)
+.L003: st %r1,__LC_SUBCHANNEL_ID
+ lpsw 0
+ sigp 0,0,0(6)
+.Ldisab: st %r14,.Ldispsw+4-.Lpg0(%r13)
+ lpsw .Ldispsw-.Lpg0(%r13)
+ .align 8
+.Lclkcmp: .quad 0x0000000000000000
+.Lall: .long 0xff000000
+.Ldump_pfx: .long dump_prefix_page
+ .align 8
+.Lnewpsw: .long 0x00080000,0x80000000+.Lpg1
+.Lpcnew: .long 0x00080000,0x80000000+.Lecs
+.Lionew: .long 0x00080000,0x80000000+.Lcont
+.Lwaitpsw: .long 0x020a0000,0x00000000+.Ltpi
+.Ldispsw: .long 0x000a0000,0x00000000
+.Liplccws: .long 0x02000000,0x60000018
+ .long 0x08000008,0x20000001
+.Liplorb: .long 0x0049504c,0x0040ff80
+ .long 0x00000000+.Liplccws
+.Lschib: .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+.Liplirb: .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
diff --git a/arch/s390/kernel/reipl64.S b/arch/s390/kernel/reipl64.S
new file mode 100644
index 00000000..36b32658
--- /dev/null
+++ b/arch/s390/kernel/reipl64.S
@@ -0,0 +1,154 @@
+/*
+ * Copyright IBM Corp 2000,2011
+ * Author(s): Holger Smolinski <Holger.Smolinski@de.ibm.com>,
+ * Denis Joseph Barrow,
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+
+#
+# store_status
+#
+# Prerequisites to run this function:
+# - Prefix register is set to zero
+# - Original prefix register is stored in "dump_prefix_page"
+# - Lowcore protection is off
+#
+ENTRY(store_status)
+ /* Save register one and load save area base */
+ stg %r1,__LC_SAVE_AREA_RESTART
+ lghi %r1,SAVE_AREA_BASE
+ /* General purpose registers */
+ stmg %r0,%r15,__LC_GPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ lg %r2,__LC_SAVE_AREA_RESTART
+ stg %r2,__LC_GPREGS_SAVE_AREA-SAVE_AREA_BASE+8(%r1)
+ /* Control registers */
+ stctg %c0,%c15,__LC_CREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ /* Access registers */
+ stam %a0,%a15,__LC_AREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ /* Floating point registers */
+ std %f0, 0x00 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f1, 0x08 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f2, 0x10 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f3, 0x18 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f4, 0x20 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f5, 0x28 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f6, 0x30 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f7, 0x38 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f8, 0x40 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f9, 0x48 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f10,0x50 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f11,0x58 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f12,0x60 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f13,0x68 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f14,0x70 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ std %f15,0x78 + __LC_FPREGS_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ /* Floating point control register */
+ stfpc __LC_FP_CREG_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ /* CPU timer */
+ stpt __LC_CPU_TIMER_SAVE_AREA-SAVE_AREA_BASE(%r1)
+ /* Saved prefix register */
+ larl %r2,dump_prefix_page
+ mvc __LC_PREFIX_SAVE_AREA-SAVE_AREA_BASE(4,%r1),0(%r2)
+ /* Clock comparator - seven bytes */
+ larl %r2,.Lclkcmp
+ stckc 0(%r2)
+ mvc __LC_CLOCK_COMP_SAVE_AREA-SAVE_AREA_BASE + 1(7,%r1),1(%r2)
+ /* Program status word */
+ epsw %r2,%r3
+ st %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 0(%r1)
+ st %r3,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 4(%r1)
+ larl %r2,store_status
+ stg %r2,__LC_PSW_SAVE_AREA-SAVE_AREA_BASE + 8(%r1)
+ br %r14
+
+ .section .bss
+ .align 8
+.Lclkcmp: .quad 0x0000000000000000
+ .previous
+
+#
+# do_reipl_asm
+# Parameter: r2 = schid of reipl device
+#
+
+ENTRY(do_reipl_asm)
+ basr %r13,0
+.Lpg0: lpswe .Lnewpsw-.Lpg0(%r13)
+.Lpg1: brasl %r14,store_status
+
+ lctlg %c6,%c6,.Lall-.Lpg0(%r13)
+ lgr %r1,%r2
+ mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13)
+ stsch .Lschib-.Lpg0(%r13)
+ oi .Lschib+5-.Lpg0(%r13),0x84
+.Lecs: xi .Lschib+27-.Lpg0(%r13),0x01
+ msch .Lschib-.Lpg0(%r13)
+ lghi %r0,5
+.Lssch: ssch .Liplorb-.Lpg0(%r13)
+ jz .L001
+ brct %r0,.Lssch
+ bas %r14,.Ldisab-.Lpg0(%r13)
+.L001: mvc __LC_IO_NEW_PSW(16),.Lionew-.Lpg0(%r13)
+.Ltpi: lpswe .Lwaitpsw-.Lpg0(%r13)
+.Lcont: c %r1,__LC_SUBCHANNEL_ID
+ jnz .Ltpi
+ clc __LC_IO_INT_PARM(4),.Liplorb-.Lpg0(%r13)
+ jnz .Ltpi
+ tsch .Liplirb-.Lpg0(%r13)
+ tm .Liplirb+9-.Lpg0(%r13),0xbf
+ jz .L002
+ bas %r14,.Ldisab-.Lpg0(%r13)
+.L002: tm .Liplirb+8-.Lpg0(%r13),0xf3
+ jz .L003
+ bas %r14,.Ldisab-.Lpg0(%r13)
+.L003: st %r1,__LC_SUBCHANNEL_ID
+ lhi %r1,0 # mode 0 = esa
+ slr %r0,%r0 # set cpuid to zero
+ sigp %r1,%r0,0x12 # switch to esa mode
+ lpsw 0
+.Ldisab: sll %r14,1
+ srl %r14,1 # need to kill hi bit to avoid specification exceptions.
+ st %r14,.Ldispsw+12-.Lpg0(%r13)
+ lpswe .Ldispsw-.Lpg0(%r13)
+ .align 8
+.Lall: .quad 0x00000000ff000000
+ .align 16
+/*
+ * These addresses have to be 31 bit otherwise
+ * the sigp will throw a specifcation exception
+ * when switching to ESA mode as bit 31 be set
+ * in the ESA psw.
+ * Bit 31 of the addresses has to be 0 for the
+ * 31bit lpswe instruction a fact they appear to have
+ * omitted from the pop.
+ */
+.Lnewpsw: .quad 0x0000000080000000
+ .quad .Lpg1
+.Lpcnew: .quad 0x0000000080000000
+ .quad .Lecs
+.Lionew: .quad 0x0000000080000000
+ .quad .Lcont
+.Lwaitpsw: .quad 0x0202000080000000
+ .quad .Ltpi
+.Ldispsw: .quad 0x0002000080000000
+ .quad 0x0000000000000000
+.Liplccws: .long 0x02000000,0x60000018
+ .long 0x08000008,0x20000001
+.Liplorb: .long 0x0049504c,0x0040ff80
+ .long 0x00000000+.Liplccws
+.Lschib: .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+.Liplirb: .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
+ .long 0x00000000,0x00000000
diff --git a/arch/s390/kernel/relocate_kernel.S b/arch/s390/kernel/relocate_kernel.S
new file mode 100644
index 00000000..c91d70ae
--- /dev/null
+++ b/arch/s390/kernel/relocate_kernel.S
@@ -0,0 +1,119 @@
+/*
+ * arch/s390/kernel/relocate_kernel.S
+ *
+ * (C) Copyright IBM Corp. 2005
+ *
+ * Author(s): Rolf Adelsberger,
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ *
+ */
+
+#include <linux/linkage.h>
+
+/*
+ * moves the new kernel to its destination...
+ * %r2 = pointer to first kimage_entry_t
+ * %r3 = start address - where to jump to after the job is done...
+ *
+ * %r5 will be used as temp. storage
+ * %r6 holds the destination address
+ * %r7 = PAGE_SIZE
+ * %r8 holds the source address
+ * %r9 = PAGE_SIZE
+ * %r10 is a page mask
+ */
+
+ .text
+ENTRY(relocate_kernel)
+ basr %r13,0 # base address
+ .base:
+ stnsm sys_msk-.base(%r13),0xfb # disable DAT
+ stctl %c0,%c15,ctlregs-.base(%r13)
+ stm %r0,%r15,gprregs-.base(%r13)
+ la %r1,load_psw-.base(%r13)
+ mvc 0(8,%r0),0(%r1)
+ la %r0,.back-.base(%r13)
+ st %r0,4(%r0)
+ oi 4(%r0),0x80
+ mvc 0x68(8,%r0),0(%r1)
+ la %r0,.back_pgm-.base(%r13)
+ st %r0,0x6c(%r0)
+ oi 0x6c(%r0),0x80
+ lhi %r0,0
+ diag %r0,%r0,0x308
+ .back:
+ basr %r13,0
+ .back_base:
+ oi have_diag308-.back_base(%r13),0x01
+ lctl %c0,%c15,ctlregs-.back_base(%r13)
+ lm %r0,%r15,gprregs-.back_base(%r13)
+ j .start_reloc
+ .back_pgm:
+ lm %r0,%r15,gprregs-.base(%r13)
+ .start_reloc:
+ lhi %r10,-1 # preparing the mask
+ sll %r10,12 # shift it such that it becomes 0xf000
+ .top:
+ lhi %r7,4096 # load PAGE_SIZE in r7
+ lhi %r9,4096 # load PAGE_SIZE in r9
+ l %r5,0(%r2) # read another word for indirection page
+ ahi %r2,4 # increment pointer
+ tml %r5,0x1 # is it a destination page?
+ je .indir_check # NO, goto "indir_check"
+ lr %r6,%r5 # r6 = r5
+ nr %r6,%r10 # mask it out and...
+ j .top # ...next iteration
+ .indir_check:
+ tml %r5,0x2 # is it a indirection page?
+ je .done_test # NO, goto "done_test"
+ nr %r5,%r10 # YES, mask out,
+ lr %r2,%r5 # move it into the right register,
+ j .top # and read next...
+ .done_test:
+ tml %r5,0x4 # is it the done indicator?
+ je .source_test # NO! Well, then it should be the source indicator...
+ j .done # ok, lets finish it here...
+ .source_test:
+ tml %r5,0x8 # it should be a source indicator...
+ je .top # NO, ignore it...
+ lr %r8,%r5 # r8 = r5
+ nr %r8,%r10 # masking
+ 0: mvcle %r6,%r8,0x0 # copy PAGE_SIZE bytes from r8 to r6 - pad with 0
+ jo 0b
+ j .top
+ .done:
+ sr %r0,%r0 # clear register r0
+ la %r4,load_psw-.base(%r13) # load psw-address into the register
+ o %r3,4(%r4) # or load address into psw
+ st %r3,4(%r4)
+ mvc 0(8,%r0),0(%r4) # copy psw to absolute address 0
+ tm have_diag308-.base(%r13),0x01
+ jno .no_diag308
+ diag %r0,%r0,0x308
+ .no_diag308:
+ sr %r1,%r1 # clear %r1
+ sr %r2,%r2 # clear %r2
+ sigp %r1,%r2,0x12 # set cpuid to zero
+ lpsw 0 # hopefully start new kernel...
+
+ .align 8
+ load_psw:
+ .long 0x00080000,0x80000000
+ sys_msk:
+ .quad 0
+ ctlregs:
+ .rept 16
+ .long 0
+ .endr
+ gprregs:
+ .rept 16
+ .long 0
+ .endr
+ have_diag308:
+ .byte 0
+ .align 8
+ relocate_kernel_end:
+ .align 8
+ .globl relocate_kernel_len
+ relocate_kernel_len:
+ .quad relocate_kernel_end - relocate_kernel
diff --git a/arch/s390/kernel/relocate_kernel64.S b/arch/s390/kernel/relocate_kernel64.S
new file mode 100644
index 00000000..7c3ce589
--- /dev/null
+++ b/arch/s390/kernel/relocate_kernel64.S
@@ -0,0 +1,122 @@
+/*
+ * arch/s390/kernel/relocate_kernel64.S
+ *
+ * (C) Copyright IBM Corp. 2005
+ *
+ * Author(s): Rolf Adelsberger,
+ * Heiko Carstens <heiko.carstens@de.ibm.com>
+ *
+ */
+
+#include <linux/linkage.h>
+
+/*
+ * moves the new kernel to its destination...
+ * %r2 = pointer to first kimage_entry_t
+ * %r3 = start address - where to jump to after the job is done...
+ *
+ * %r5 will be used as temp. storage
+ * %r6 holds the destination address
+ * %r7 = PAGE_SIZE
+ * %r8 holds the source address
+ * %r9 = PAGE_SIZE
+ *
+ * 0xf000 is a page_mask
+ */
+
+ .text
+ENTRY(relocate_kernel)
+ basr %r13,0 # base address
+ .base:
+ stnsm sys_msk-.base(%r13),0xfb # disable DAT
+ stctg %c0,%c15,ctlregs-.base(%r13)
+ stmg %r0,%r15,gprregs-.base(%r13)
+ lghi %r0,3
+ sllg %r0,%r0,31
+ stg %r0,0x1d0(%r0)
+ la %r0,.back_pgm-.base(%r13)
+ stg %r0,0x1d8(%r0)
+ la %r1,load_psw-.base(%r13)
+ mvc 0(8,%r0),0(%r1)
+ la %r0,.back-.base(%r13)
+ st %r0,4(%r0)
+ oi 4(%r0),0x80
+ lghi %r0,0
+ diag %r0,%r0,0x308
+ .back:
+ lhi %r1,1 # mode 1 = esame
+ sigp %r1,%r0,0x12 # switch to esame mode
+ sam64 # switch to 64 bit addressing mode
+ basr %r13,0
+ .back_base:
+ oi have_diag308-.back_base(%r13),0x01
+ lctlg %c0,%c15,ctlregs-.back_base(%r13)
+ lmg %r0,%r15,gprregs-.back_base(%r13)
+ j .top
+ .back_pgm:
+ lmg %r0,%r15,gprregs-.base(%r13)
+ .top:
+ lghi %r7,4096 # load PAGE_SIZE in r7
+ lghi %r9,4096 # load PAGE_SIZE in r9
+ lg %r5,0(%r2) # read another word for indirection page
+ aghi %r2,8 # increment pointer
+ tml %r5,0x1 # is it a destination page?
+ je .indir_check # NO, goto "indir_check"
+ lgr %r6,%r5 # r6 = r5
+ nill %r6,0xf000 # mask it out and...
+ j .top # ...next iteration
+ .indir_check:
+ tml %r5,0x2 # is it a indirection page?
+ je .done_test # NO, goto "done_test"
+ nill %r5,0xf000 # YES, mask out,
+ lgr %r2,%r5 # move it into the right register,
+ j .top # and read next...
+ .done_test:
+ tml %r5,0x4 # is it the done indicator?
+ je .source_test # NO! Well, then it should be the source indicator...
+ j .done # ok, lets finish it here...
+ .source_test:
+ tml %r5,0x8 # it should be a source indicator...
+ je .top # NO, ignore it...
+ lgr %r8,%r5 # r8 = r5
+ nill %r8,0xf000 # masking
+ 0: mvcle %r6,%r8,0x0 # copy PAGE_SIZE bytes from r8 to r6 - pad with 0
+ jo 0b
+ j .top
+ .done:
+ sgr %r0,%r0 # clear register r0
+ la %r4,load_psw-.base(%r13) # load psw-address into the register
+ o %r3,4(%r4) # or load address into psw
+ st %r3,4(%r4)
+ mvc 0(8,%r0),0(%r4) # copy psw to absolute address 0
+ tm have_diag308-.base(%r13),0x01
+ jno .no_diag308
+ diag %r0,%r0,0x308
+ .no_diag308:
+ sam31 # 31 bit mode
+ sr %r1,%r1 # erase register r1
+ sr %r2,%r2 # erase register r2
+ sigp %r1,%r2,0x12 # set cpuid to zero
+ lpsw 0 # hopefully start new kernel...
+
+ .align 8
+ load_psw:
+ .long 0x00080000,0x80000000
+ sys_msk:
+ .quad 0
+ ctlregs:
+ .rept 16
+ .quad 0
+ .endr
+ gprregs:
+ .rept 16
+ .quad 0
+ .endr
+ have_diag308:
+ .byte 0
+ .align 8
+ relocate_kernel_end:
+ .align 8
+ .globl relocate_kernel_len
+ relocate_kernel_len:
+ .quad relocate_kernel_end - relocate_kernel
diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c
new file mode 100644
index 00000000..57b53664
--- /dev/null
+++ b/arch/s390/kernel/s390_ksyms.c
@@ -0,0 +1,10 @@
+#include <linux/module.h>
+#include <linux/kvm_host.h>
+#include <asm/ftrace.h>
+
+#ifdef CONFIG_FUNCTION_TRACER
+EXPORT_SYMBOL(_mcount);
+#endif
+#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
+EXPORT_SYMBOL(sie64a);
+#endif
diff --git a/arch/s390/kernel/sclp.S b/arch/s390/kernel/sclp.S
new file mode 100644
index 00000000..95792d84
--- /dev/null
+++ b/arch/s390/kernel/sclp.S
@@ -0,0 +1,353 @@
+/*
+ * Mini SCLP driver.
+ *
+ * Copyright IBM Corp. 2004,2009
+ *
+ * Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>,
+ * Heiko Carstens <heiko.carstens@de.ibm.com>,
+ *
+ */
+
+#include <linux/linkage.h>
+
+LC_EXT_NEW_PSW = 0x58 # addr of ext int handler
+LC_EXT_NEW_PSW_64 = 0x1b0 # addr of ext int handler 64 bit
+LC_EXT_INT_PARAM = 0x80 # addr of ext int parameter
+LC_EXT_INT_CODE = 0x86 # addr of ext int code
+LC_AR_MODE_ID = 0xa3
+
+#
+# Subroutine which waits synchronously until either an external interruption
+# or a timeout occurs.
+#
+# Parameters:
+# R2 = 0 for no timeout, non-zero for timeout in (approximated) seconds
+#
+# Returns:
+# R2 = 0 on interrupt, 2 on timeout
+# R3 = external interruption parameter if R2=0
+#
+
+_sclp_wait_int:
+ stm %r6,%r15,24(%r15) # save registers
+ basr %r13,0 # get base register
+.LbaseS1:
+ ahi %r15,-96 # create stack frame
+ la %r8,LC_EXT_NEW_PSW # register int handler
+ la %r9,.LextpswS1-.LbaseS1(%r13)
+#ifdef CONFIG_64BIT
+ tm LC_AR_MODE_ID,1
+ jno .Lesa1
+ la %r8,LC_EXT_NEW_PSW_64 # register int handler 64 bit
+ la %r9,.LextpswS1_64-.LbaseS1(%r13)
+.Lesa1:
+#endif
+ mvc .LoldpswS1-.LbaseS1(16,%r13),0(%r8)
+ mvc 0(16,%r8),0(%r9)
+ lhi %r6,0x0200 # cr mask for ext int (cr0.54)
+ ltr %r2,%r2
+ jz .LsetctS1
+ ahi %r6,0x0800 # cr mask for clock int (cr0.52)
+ stck .LtimeS1-.LbaseS1(%r13) # initiate timeout
+ al %r2,.LtimeS1-.LbaseS1(%r13)
+ st %r2,.LtimeS1-.LbaseS1(%r13)
+ sckc .LtimeS1-.LbaseS1(%r13)
+
+.LsetctS1:
+ stctl %c0,%c0,.LctlS1-.LbaseS1(%r13) # enable required interrupts
+ l %r0,.LctlS1-.LbaseS1(%r13)
+ lhi %r1,~(0x200 | 0x800) # clear old values
+ nr %r1,%r0
+ or %r1,%r6 # set new value
+ st %r1,.LctlS1-.LbaseS1(%r13)
+ lctl %c0,%c0,.LctlS1-.LbaseS1(%r13)
+ st %r0,.LctlS1-.LbaseS1(%r13)
+ lhi %r2,2 # return code for timeout
+.LloopS1:
+ lpsw .LwaitpswS1-.LbaseS1(%r13) # wait until interrupt
+.LwaitS1:
+ lh %r7,LC_EXT_INT_CODE
+ chi %r7,0x1004 # timeout?
+ je .LtimeoutS1
+ chi %r7,0x2401 # service int?
+ jne .LloopS1
+ sr %r2,%r2
+ l %r3,LC_EXT_INT_PARAM
+.LtimeoutS1:
+ lctl %c0,%c0,.LctlS1-.LbaseS1(%r13) # restore interrupt setting
+ # restore old handler
+ mvc 0(16,%r8),.LoldpswS1-.LbaseS1(%r13)
+ lm %r6,%r15,120(%r15) # restore registers
+ br %r14 # return to caller
+
+ .align 8
+.LoldpswS1:
+ .long 0, 0, 0, 0 # old ext int PSW
+.LextpswS1:
+ .long 0x00080000, 0x80000000+.LwaitS1 # PSW to handle ext int
+#ifdef CONFIG_64BIT
+.LextpswS1_64:
+ .quad 0x0000000180000000, .LwaitS1 # PSW to handle ext int, 64 bit
+#endif
+.LwaitpswS1:
+ .long 0x010a0000, 0x00000000+.LloopS1 # PSW to wait for ext int
+.LtimeS1:
+ .quad 0 # current time
+.LctlS1:
+ .long 0 # CT0 contents
+
+#
+# Subroutine to synchronously issue a service call.
+#
+# Parameters:
+# R2 = command word
+# R3 = sccb address
+#
+# Returns:
+# R2 = 0 on success, 1 on failure
+# R3 = sccb response code if R2 = 0
+#
+
+_sclp_servc:
+ stm %r6,%r15,24(%r15) # save registers
+ ahi %r15,-96 # create stack frame
+ lr %r6,%r2 # save command word
+ lr %r7,%r3 # save sccb address
+.LretryS2:
+ lhi %r2,1 # error return code
+ .insn rre,0xb2200000,%r6,%r7 # servc
+ brc 1,.LendS2 # exit if not operational
+ brc 8,.LnotbusyS2 # go on if not busy
+ sr %r2,%r2 # wait until no longer busy
+ bras %r14,_sclp_wait_int
+ j .LretryS2 # retry
+.LnotbusyS2:
+ sr %r2,%r2 # wait until result
+ bras %r14,_sclp_wait_int
+ sr %r2,%r2
+ lh %r3,6(%r7)
+.LendS2:
+ lm %r6,%r15,120(%r15) # restore registers
+ br %r14
+
+#
+# Subroutine to set up the SCLP interface.
+#
+# Parameters:
+# R2 = 0 to activate, non-zero to deactivate
+#
+# Returns:
+# R2 = 0 on success, non-zero on failure
+#
+
+_sclp_setup:
+ stm %r6,%r15,24(%r15) # save registers
+ ahi %r15,-96 # create stack frame
+ basr %r13,0 # get base register
+.LbaseS3:
+ l %r6,.LsccbS0-.LbaseS3(%r13) # prepare init mask sccb
+ mvc 0(.LinitendS3-.LinitsccbS3,%r6),.LinitsccbS3-.LbaseS3(%r13)
+ ltr %r2,%r2 # initialization?
+ jz .LdoinitS3 # go ahead
+ # clear masks
+ xc .LinitmaskS3-.LinitsccbS3(8,%r6),.LinitmaskS3-.LinitsccbS3(%r6)
+.LdoinitS3:
+ l %r2,.LwritemaskS3-.LbaseS3(%r13)# get command word
+ lr %r3,%r6 # get sccb address
+ bras %r14,_sclp_servc # issue service call
+ ltr %r2,%r2 # servc successful?
+ jnz .LerrorS3
+ chi %r3,0x20 # write mask successful?
+ jne .LerrorS3
+ # check masks
+ la %r2,.LinitmaskS3-.LinitsccbS3(%r6)
+ l %r1,0(%r2) # receive mask ok?
+ n %r1,12(%r2)
+ cl %r1,0(%r2)
+ jne .LerrorS3
+ l %r1,4(%r2) # send mask ok?
+ n %r1,8(%r2)
+ cl %r1,4(%r2)
+ sr %r2,%r2
+ je .LendS3
+.LerrorS3:
+ lhi %r2,1 # error return code
+.LendS3:
+ lm %r6,%r15,120(%r15) # restore registers
+ br %r14
+.LwritemaskS3:
+ .long 0x00780005 # SCLP command for write mask
+.LinitsccbS3:
+ .word .LinitendS3-.LinitsccbS3
+ .byte 0,0,0,0
+ .word 0
+ .word 0
+ .word 4
+.LinitmaskS3:
+ .long 0x80000000
+ .long 0x40000000
+ .long 0
+ .long 0
+.LinitendS3:
+
+#
+# Subroutine which prints a given text to the SCLP console.
+#
+# Parameters:
+# R2 = address of nil-terminated ASCII text
+#
+# Returns:
+# R2 = 0 on success, 1 on failure
+#
+
+_sclp_print:
+ stm %r6,%r15,24(%r15) # save registers
+ ahi %r15,-96 # create stack frame
+ basr %r13,0 # get base register
+.LbaseS4:
+ l %r8,.LsccbS0-.LbaseS4(%r13) # prepare write data sccb
+ mvc 0(.LmtoS4-.LwritesccbS4,%r8),.LwritesccbS4-.LbaseS4(%r13)
+ la %r7,.LmtoS4-.LwritesccbS4(%r8) # current mto addr
+ sr %r0,%r0
+ l %r10,.Lascebc-.LbaseS4(%r13) # address of translation table
+.LinitmtoS4:
+ # initialize mto
+ mvc 0(.LmtoendS4-.LmtoS4,%r7),.LmtoS4-.LbaseS4(%r13)
+ lhi %r6,.LmtoendS4-.LmtoS4 # current mto length
+.LloopS4:
+ ic %r0,0(%r2) # get character
+ ahi %r2,1
+ ltr %r0,%r0 # end of string?
+ jz .LfinalizemtoS4
+ chi %r0,0x15 # end of line (NL)?
+ jz .LfinalizemtoS4
+ stc %r0,0(%r6,%r7) # copy to mto
+ la %r11,0(%r6,%r7)
+ tr 0(1,%r11),0(%r10) # translate to EBCDIC
+ ahi %r6,1
+ j .LloopS4
+.LfinalizemtoS4:
+ sth %r6,0(%r7) # update mto length
+ lh %r9,.LmdbS4-.LwritesccbS4(%r8) # update mdb length
+ ar %r9,%r6
+ sth %r9,.LmdbS4-.LwritesccbS4(%r8)
+ lh %r9,.LevbufS4-.LwritesccbS4(%r8)# update evbuf length
+ ar %r9,%r6
+ sth %r9,.LevbufS4-.LwritesccbS4(%r8)
+ lh %r9,0(%r8) # update sccb length
+ ar %r9,%r6
+ sth %r9,0(%r8)
+ ar %r7,%r6 # update current mto address
+ ltr %r0,%r0 # more characters?
+ jnz .LinitmtoS4
+ l %r2,.LwritedataS4-.LbaseS4(%r13)# write data
+ lr %r3,%r8
+ bras %r14,_sclp_servc
+ ltr %r2,%r2 # servc successful?
+ jnz .LendS4
+ chi %r3,0x20 # write data successful?
+ je .LendS4
+ lhi %r2,1 # error return code
+.LendS4:
+ lm %r6,%r15,120(%r15) # restore registers
+ br %r14
+
+#
+# Function which prints a given text to the SCLP console.
+#
+# Parameters:
+# R2 = address of nil-terminated ASCII text
+#
+# Returns:
+# R2 = 0 on success, 1 on failure
+#
+
+ENTRY(_sclp_print_early)
+ stm %r6,%r15,24(%r15) # save registers
+ ahi %r15,-96 # create stack frame
+#ifdef CONFIG_64BIT
+ tm LC_AR_MODE_ID,1
+ jno .Lesa2
+ ahi %r15,-80
+ stmh %r6,%r15,96(%r15) # store upper register halves
+.Lesa2:
+#endif
+ lr %r10,%r2 # save string pointer
+ lhi %r2,0
+ bras %r14,_sclp_setup # enable console
+ ltr %r2,%r2
+ jnz .LendS5
+ lr %r2,%r10
+ bras %r14,_sclp_print # print string
+ ltr %r2,%r2
+ jnz .LendS5
+ lhi %r2,1
+ bras %r14,_sclp_setup # disable console
+.LendS5:
+#ifdef CONFIG_64BIT
+ tm LC_AR_MODE_ID,1
+ jno .Lesa3
+ lmh %r6,%r15,96(%r15) # store upper register halves
+ ahi %r15,80
+.Lesa3:
+#endif
+ lm %r6,%r15,120(%r15) # restore registers
+ br %r14
+
+.LwritedataS4:
+ .long 0x00760005 # SCLP command for write data
+.LwritesccbS4:
+ # sccb
+ .word .LmtoS4-.LwritesccbS4
+ .byte 0
+ .byte 0,0,0
+ .word 0
+
+ # evbuf
+.LevbufS4:
+ .word .LmtoS4-.LevbufS4
+ .byte 0x02
+ .byte 0
+ .word 0
+
+.LmdbS4:
+ # mdb
+ .word .LmtoS4-.LmdbS4
+ .word 1
+ .long 0xd4c4c240
+ .long 1
+
+ # go
+.LgoS4:
+ .word .LmtoS4-.LgoS4
+ .word 1
+ .long 0
+ .byte 0,0,0,0,0,0,0,0
+ .byte 0,0,0
+ .byte 0
+ .byte 0,0,0,0,0,0,0
+ .byte 0
+ .word 0
+ .byte 0,0,0,0,0,0,0,0,0,0
+ .byte 0,0,0,0,0,0,0,0
+ .byte 0,0,0,0,0,0,0,0
+
+.LmtoS4:
+ .word .LmtoendS4-.LmtoS4
+ .word 4
+ .word 0x1000
+ .byte 0
+ .byte 0,0,0
+.LmtoendS4:
+
+ # Global constants
+.LsccbS0:
+ .long _sclp_work_area
+.Lascebc:
+ .long _ascebc
+
+.section .data,"aw",@progbits
+ .balign 4096
+_sclp_work_area:
+ .fill 4096
+.previous
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
new file mode 100644
index 00000000..06264ae8
--- /dev/null
+++ b/arch/s390/kernel/setup.c
@@ -0,0 +1,1092 @@
+/*
+ * arch/s390/kernel/setup.c
+ *
+ * S390 version
+ * Copyright (C) IBM Corp. 1999,2012
+ * Author(s): Hartmut Penner (hp@de.ibm.com),
+ * Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ * Derived from "arch/i386/kernel/setup.c"
+ * Copyright (C) 1995, Linus Torvalds
+ */
+
+/*
+ * This file handles the architecture-dependent parts of initialization
+ */
+
+#define KMSG_COMPONENT "setup"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/memblock.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/tty.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/initrd.h>
+#include <linux/bootmem.h>
+#include <linux/root_dev.h>
+#include <linux/console.h>
+#include <linux/kernel_stat.h>
+#include <linux/device.h>
+#include <linux/notifier.h>
+#include <linux/pfn.h>
+#include <linux/ctype.h>
+#include <linux/reboot.h>
+#include <linux/topology.h>
+#include <linux/ftrace.h>
+#include <linux/kexec.h>
+#include <linux/crash_dump.h>
+#include <linux/memory.h>
+#include <linux/compat.h>
+
+#include <asm/ipl.h>
+#include <asm/uaccess.h>
+#include <asm/facility.h>
+#include <asm/smp.h>
+#include <asm/mmu_context.h>
+#include <asm/cpcmd.h>
+#include <asm/lowcore.h>
+#include <asm/irq.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/sections.h>
+#include <asm/ebcdic.h>
+#include <asm/kvm_virtio.h>
+#include <asm/diag.h>
+#include <asm/os_info.h>
+#include "entry.h"
+
+long psw_kernel_bits = PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_ASC_PRIMARY |
+ PSW_MASK_EA | PSW_MASK_BA;
+long psw_user_bits = PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT |
+ PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_MCHECK |
+ PSW_MASK_PSTATE | PSW_ASC_HOME;
+
+/*
+ * User copy operations.
+ */
+struct uaccess_ops uaccess;
+EXPORT_SYMBOL(uaccess);
+
+/*
+ * Machine setup..
+ */
+unsigned int console_mode = 0;
+EXPORT_SYMBOL(console_mode);
+
+unsigned int console_devno = -1;
+EXPORT_SYMBOL(console_devno);
+
+unsigned int console_irq = -1;
+EXPORT_SYMBOL(console_irq);
+
+unsigned long elf_hwcap = 0;
+char elf_platform[ELF_PLATFORM_SIZE];
+
+struct mem_chunk __initdata memory_chunk[MEMORY_CHUNKS];
+
+int __initdata memory_end_set;
+unsigned long __initdata memory_end;
+
+unsigned long VMALLOC_START;
+EXPORT_SYMBOL(VMALLOC_START);
+
+unsigned long VMALLOC_END;
+EXPORT_SYMBOL(VMALLOC_END);
+
+struct page *vmemmap;
+EXPORT_SYMBOL(vmemmap);
+
+/* An array with a pointer to the lowcore of every CPU. */
+struct _lowcore *lowcore_ptr[NR_CPUS];
+EXPORT_SYMBOL(lowcore_ptr);
+
+/*
+ * This is set up by the setup-routine at boot-time
+ * for S390 need to find out, what we have to setup
+ * using address 0x10400 ...
+ */
+
+#include <asm/setup.h>
+
+/*
+ * condev= and conmode= setup parameter.
+ */
+
+static int __init condev_setup(char *str)
+{
+ int vdev;
+
+ vdev = simple_strtoul(str, &str, 0);
+ if (vdev >= 0 && vdev < 65536) {
+ console_devno = vdev;
+ console_irq = -1;
+ }
+ return 1;
+}
+
+__setup("condev=", condev_setup);
+
+static void __init set_preferred_console(void)
+{
+ if (MACHINE_IS_KVM)
+ add_preferred_console("hvc", 0, NULL);
+ else if (CONSOLE_IS_3215 || CONSOLE_IS_SCLP)
+ add_preferred_console("ttyS", 0, NULL);
+ else if (CONSOLE_IS_3270)
+ add_preferred_console("tty3270", 0, NULL);
+}
+
+static int __init conmode_setup(char *str)
+{
+#if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
+ if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
+ SET_CONSOLE_SCLP;
+#endif
+#if defined(CONFIG_TN3215_CONSOLE)
+ if (strncmp(str, "3215", 5) == 0)
+ SET_CONSOLE_3215;
+#endif
+#if defined(CONFIG_TN3270_CONSOLE)
+ if (strncmp(str, "3270", 5) == 0)
+ SET_CONSOLE_3270;
+#endif
+ set_preferred_console();
+ return 1;
+}
+
+__setup("conmode=", conmode_setup);
+
+static void __init conmode_default(void)
+{
+ char query_buffer[1024];
+ char *ptr;
+
+ if (MACHINE_IS_VM) {
+ cpcmd("QUERY CONSOLE", query_buffer, 1024, NULL);
+ console_devno = simple_strtoul(query_buffer + 5, NULL, 16);
+ ptr = strstr(query_buffer, "SUBCHANNEL =");
+ console_irq = simple_strtoul(ptr + 13, NULL, 16);
+ cpcmd("QUERY TERM", query_buffer, 1024, NULL);
+ ptr = strstr(query_buffer, "CONMODE");
+ /*
+ * Set the conmode to 3215 so that the device recognition
+ * will set the cu_type of the console to 3215. If the
+ * conmode is 3270 and we don't set it back then both
+ * 3215 and the 3270 driver will try to access the console
+ * device (3215 as console and 3270 as normal tty).
+ */
+ cpcmd("TERM CONMODE 3215", NULL, 0, NULL);
+ if (ptr == NULL) {
+#if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
+ SET_CONSOLE_SCLP;
+#endif
+ return;
+ }
+ if (strncmp(ptr + 8, "3270", 4) == 0) {
+#if defined(CONFIG_TN3270_CONSOLE)
+ SET_CONSOLE_3270;
+#elif defined(CONFIG_TN3215_CONSOLE)
+ SET_CONSOLE_3215;
+#elif defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
+ SET_CONSOLE_SCLP;
+#endif
+ } else if (strncmp(ptr + 8, "3215", 4) == 0) {
+#if defined(CONFIG_TN3215_CONSOLE)
+ SET_CONSOLE_3215;
+#elif defined(CONFIG_TN3270_CONSOLE)
+ SET_CONSOLE_3270;
+#elif defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
+ SET_CONSOLE_SCLP;
+#endif
+ }
+ } else {
+#if defined(CONFIG_SCLP_CONSOLE) || defined(CONFIG_SCLP_VT220_CONSOLE)
+ SET_CONSOLE_SCLP;
+#endif
+ }
+}
+
+#ifdef CONFIG_ZFCPDUMP
+static void __init setup_zfcpdump(unsigned int console_devno)
+{
+ static char str[41];
+
+ if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+ return;
+ if (OLDMEM_BASE)
+ return;
+ if (console_devno != -1)
+ sprintf(str, " cio_ignore=all,!0.0.%04x,!0.0.%04x",
+ ipl_info.data.fcp.dev_id.devno, console_devno);
+ else
+ sprintf(str, " cio_ignore=all,!0.0.%04x",
+ ipl_info.data.fcp.dev_id.devno);
+ strcat(boot_command_line, str);
+ console_loglevel = 2;
+}
+#else
+static inline void setup_zfcpdump(unsigned int console_devno) {}
+#endif /* CONFIG_ZFCPDUMP */
+
+ /*
+ * Reboot, halt and power_off stubs. They just call _machine_restart,
+ * _machine_halt or _machine_power_off.
+ */
+
+void machine_restart(char *command)
+{
+ if ((!in_interrupt() && !in_atomic()) || oops_in_progress)
+ /*
+ * Only unblank the console if we are called in enabled
+ * context or a bust_spinlocks cleared the way for us.
+ */
+ console_unblank();
+ _machine_restart(command);
+}
+
+void machine_halt(void)
+{
+ if (!in_interrupt() || oops_in_progress)
+ /*
+ * Only unblank the console if we are called in enabled
+ * context or a bust_spinlocks cleared the way for us.
+ */
+ console_unblank();
+ _machine_halt();
+}
+
+void machine_power_off(void)
+{
+ if (!in_interrupt() || oops_in_progress)
+ /*
+ * Only unblank the console if we are called in enabled
+ * context or a bust_spinlocks cleared the way for us.
+ */
+ console_unblank();
+ _machine_power_off();
+}
+
+/*
+ * Dummy power off function.
+ */
+void (*pm_power_off)(void) = machine_power_off;
+
+static int __init early_parse_mem(char *p)
+{
+ memory_end = memparse(p, &p);
+ memory_end_set = 1;
+ return 0;
+}
+early_param("mem", early_parse_mem);
+
+static int __init parse_vmalloc(char *arg)
+{
+ if (!arg)
+ return -EINVAL;
+ VMALLOC_END = (memparse(arg, &arg) + PAGE_SIZE - 1) & PAGE_MASK;
+ return 0;
+}
+early_param("vmalloc", parse_vmalloc);
+
+unsigned int user_mode = HOME_SPACE_MODE;
+EXPORT_SYMBOL_GPL(user_mode);
+
+static int set_amode_primary(void)
+{
+ psw_kernel_bits = (psw_kernel_bits & ~PSW_MASK_ASC) | PSW_ASC_HOME;
+ psw_user_bits = (psw_user_bits & ~PSW_MASK_ASC) | PSW_ASC_PRIMARY;
+#ifdef CONFIG_COMPAT
+ psw32_user_bits =
+ (psw32_user_bits & ~PSW32_MASK_ASC) | PSW32_ASC_PRIMARY;
+#endif
+
+ if (MACHINE_HAS_MVCOS) {
+ memcpy(&uaccess, &uaccess_mvcos_switch, sizeof(uaccess));
+ return 1;
+ } else {
+ memcpy(&uaccess, &uaccess_pt, sizeof(uaccess));
+ return 0;
+ }
+}
+
+/*
+ * Switch kernel/user addressing modes?
+ */
+static int __init early_parse_switch_amode(char *p)
+{
+ user_mode = PRIMARY_SPACE_MODE;
+ return 0;
+}
+early_param("switch_amode", early_parse_switch_amode);
+
+static int __init early_parse_user_mode(char *p)
+{
+ if (p && strcmp(p, "primary") == 0)
+ user_mode = PRIMARY_SPACE_MODE;
+ else if (!p || strcmp(p, "home") == 0)
+ user_mode = HOME_SPACE_MODE;
+ else
+ return 1;
+ return 0;
+}
+early_param("user_mode", early_parse_user_mode);
+
+static void setup_addressing_mode(void)
+{
+ if (user_mode == PRIMARY_SPACE_MODE) {
+ if (set_amode_primary())
+ pr_info("Address spaces switched, "
+ "mvcos available\n");
+ else
+ pr_info("Address spaces switched, "
+ "mvcos not available\n");
+ }
+}
+
+void *restart_stack __attribute__((__section__(".data")));
+
+static void __init setup_lowcore(void)
+{
+ struct _lowcore *lc;
+
+ /*
+ * Setup lowcore for boot cpu
+ */
+ BUILD_BUG_ON(sizeof(struct _lowcore) != LC_PAGES * 4096);
+ lc = __alloc_bootmem_low(LC_PAGES * PAGE_SIZE, LC_PAGES * PAGE_SIZE, 0);
+ lc->restart_psw.mask = psw_kernel_bits;
+ lc->restart_psw.addr =
+ PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
+ lc->external_new_psw.mask = psw_kernel_bits |
+ PSW_MASK_DAT | PSW_MASK_MCHECK;
+ lc->external_new_psw.addr =
+ PSW_ADDR_AMODE | (unsigned long) ext_int_handler;
+ lc->svc_new_psw.mask = psw_kernel_bits |
+ PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
+ lc->svc_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) system_call;
+ lc->program_new_psw.mask = psw_kernel_bits |
+ PSW_MASK_DAT | PSW_MASK_MCHECK;
+ lc->program_new_psw.addr =
+ PSW_ADDR_AMODE | (unsigned long) pgm_check_handler;
+ lc->mcck_new_psw.mask = psw_kernel_bits;
+ lc->mcck_new_psw.addr =
+ PSW_ADDR_AMODE | (unsigned long) mcck_int_handler;
+ lc->io_new_psw.mask = psw_kernel_bits |
+ PSW_MASK_DAT | PSW_MASK_MCHECK;
+ lc->io_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) io_int_handler;
+ lc->clock_comparator = -1ULL;
+ lc->kernel_stack = ((unsigned long) &init_thread_union) + THREAD_SIZE;
+ lc->async_stack = (unsigned long)
+ __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0) + ASYNC_SIZE;
+ lc->panic_stack = (unsigned long)
+ __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0) + PAGE_SIZE;
+ lc->current_task = (unsigned long) init_thread_union.thread_info.task;
+ lc->thread_info = (unsigned long) &init_thread_union;
+ lc->machine_flags = S390_lowcore.machine_flags;
+ lc->stfl_fac_list = S390_lowcore.stfl_fac_list;
+ memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
+ MAX_FACILITY_BIT/8);
+#ifndef CONFIG_64BIT
+ if (MACHINE_HAS_IEEE) {
+ lc->extended_save_area_addr = (__u32)
+ __alloc_bootmem_low(PAGE_SIZE, PAGE_SIZE, 0);
+ /* enable extended save area */
+ __ctl_set_bit(14, 29);
+ }
+#else
+ lc->vdso_per_cpu_data = (unsigned long) &lc->paste[0];
+#endif
+ lc->sync_enter_timer = S390_lowcore.sync_enter_timer;
+ lc->async_enter_timer = S390_lowcore.async_enter_timer;
+ lc->exit_timer = S390_lowcore.exit_timer;
+ lc->user_timer = S390_lowcore.user_timer;
+ lc->system_timer = S390_lowcore.system_timer;
+ lc->steal_timer = S390_lowcore.steal_timer;
+ lc->last_update_timer = S390_lowcore.last_update_timer;
+ lc->last_update_clock = S390_lowcore.last_update_clock;
+ lc->ftrace_func = S390_lowcore.ftrace_func;
+
+ restart_stack = __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0);
+ restart_stack += ASYNC_SIZE;
+
+ /*
+ * Set up PSW restart to call ipl.c:do_restart(). Copy the relevant
+ * restart data to the absolute zero lowcore. This is necesary if
+ * PSW restart is done on an offline CPU that has lowcore zero.
+ */
+ lc->restart_stack = (unsigned long) restart_stack;
+ lc->restart_fn = (unsigned long) do_restart;
+ lc->restart_data = 0;
+ lc->restart_source = -1UL;
+ memcpy(&S390_lowcore.restart_stack, &lc->restart_stack,
+ 4*sizeof(unsigned long));
+ copy_to_absolute_zero(&S390_lowcore.restart_psw,
+ &lc->restart_psw, sizeof(psw_t));
+
+ set_prefix((u32)(unsigned long) lc);
+ lowcore_ptr[0] = lc;
+}
+
+static struct resource code_resource = {
+ .name = "Kernel code",
+ .flags = IORESOURCE_BUSY | IORESOURCE_MEM,
+};
+
+static struct resource data_resource = {
+ .name = "Kernel data",
+ .flags = IORESOURCE_BUSY | IORESOURCE_MEM,
+};
+
+static struct resource bss_resource = {
+ .name = "Kernel bss",
+ .flags = IORESOURCE_BUSY | IORESOURCE_MEM,
+};
+
+static struct resource __initdata *standard_resources[] = {
+ &code_resource,
+ &data_resource,
+ &bss_resource,
+};
+
+static void __init setup_resources(void)
+{
+ struct resource *res, *std_res, *sub_res;
+ int i, j;
+
+ code_resource.start = (unsigned long) &_text;
+ code_resource.end = (unsigned long) &_etext - 1;
+ data_resource.start = (unsigned long) &_etext;
+ data_resource.end = (unsigned long) &_edata - 1;
+ bss_resource.start = (unsigned long) &__bss_start;
+ bss_resource.end = (unsigned long) &__bss_stop - 1;
+
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ if (!memory_chunk[i].size)
+ continue;
+ if (memory_chunk[i].type == CHUNK_OLDMEM ||
+ memory_chunk[i].type == CHUNK_CRASHK)
+ continue;
+ res = alloc_bootmem_low(sizeof(*res));
+ res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
+ switch (memory_chunk[i].type) {
+ case CHUNK_READ_WRITE:
+ case CHUNK_CRASHK:
+ res->name = "System RAM";
+ break;
+ case CHUNK_READ_ONLY:
+ res->name = "System ROM";
+ res->flags |= IORESOURCE_READONLY;
+ break;
+ default:
+ res->name = "reserved";
+ }
+ res->start = memory_chunk[i].addr;
+ res->end = res->start + memory_chunk[i].size - 1;
+ request_resource(&iomem_resource, res);
+
+ for (j = 0; j < ARRAY_SIZE(standard_resources); j++) {
+ std_res = standard_resources[j];
+ if (std_res->start < res->start ||
+ std_res->start > res->end)
+ continue;
+ if (std_res->end > res->end) {
+ sub_res = alloc_bootmem_low(sizeof(*sub_res));
+ *sub_res = *std_res;
+ sub_res->end = res->end;
+ std_res->start = res->end + 1;
+ request_resource(res, sub_res);
+ } else {
+ request_resource(res, std_res);
+ }
+ }
+ }
+}
+
+unsigned long real_memory_size;
+EXPORT_SYMBOL_GPL(real_memory_size);
+
+static void __init setup_memory_end(void)
+{
+ unsigned long vmax, vmalloc_size, tmp;
+ int i;
+
+
+#ifdef CONFIG_ZFCPDUMP
+ if (ipl_info.type == IPL_TYPE_FCP_DUMP && !OLDMEM_BASE) {
+ memory_end = ZFCPDUMP_HSA_SIZE;
+ memory_end_set = 1;
+ }
+#endif
+ real_memory_size = 0;
+ memory_end &= PAGE_MASK;
+
+ /*
+ * Make sure all chunks are MAX_ORDER aligned so we don't need the
+ * extra checks that HOLES_IN_ZONE would require.
+ */
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ unsigned long start, end;
+ struct mem_chunk *chunk;
+ unsigned long align;
+
+ chunk = &memory_chunk[i];
+ align = 1UL << (MAX_ORDER + PAGE_SHIFT - 1);
+ start = (chunk->addr + align - 1) & ~(align - 1);
+ end = (chunk->addr + chunk->size) & ~(align - 1);
+ if (start >= end)
+ memset(chunk, 0, sizeof(*chunk));
+ else {
+ chunk->addr = start;
+ chunk->size = end - start;
+ }
+ real_memory_size = max(real_memory_size,
+ chunk->addr + chunk->size);
+ }
+
+ /* Choose kernel address space layout: 2, 3, or 4 levels. */
+#ifdef CONFIG_64BIT
+ vmalloc_size = VMALLOC_END ?: 128UL << 30;
+ tmp = (memory_end ?: real_memory_size) / PAGE_SIZE;
+ tmp = tmp * (sizeof(struct page) + PAGE_SIZE) + vmalloc_size;
+ if (tmp <= (1UL << 42))
+ vmax = 1UL << 42; /* 3-level kernel page table */
+ else
+ vmax = 1UL << 53; /* 4-level kernel page table */
+#else
+ vmalloc_size = VMALLOC_END ?: 96UL << 20;
+ vmax = 1UL << 31; /* 2-level kernel page table */
+#endif
+ /* vmalloc area is at the end of the kernel address space. */
+ VMALLOC_END = vmax;
+ VMALLOC_START = vmax - vmalloc_size;
+
+ /* Split remaining virtual space between 1:1 mapping & vmemmap array */
+ tmp = VMALLOC_START / (PAGE_SIZE + sizeof(struct page));
+ tmp = VMALLOC_START - tmp * sizeof(struct page);
+ tmp &= ~((vmax >> 11) - 1); /* align to page table level */
+ tmp = min(tmp, 1UL << MAX_PHYSMEM_BITS);
+ vmemmap = (struct page *) tmp;
+
+ /* Take care that memory_end is set and <= vmemmap */
+ memory_end = min(memory_end ?: real_memory_size, tmp);
+
+ /* Fixup memory chunk array to fit into 0..memory_end */
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ struct mem_chunk *chunk = &memory_chunk[i];
+
+ if (chunk->addr >= memory_end) {
+ memset(chunk, 0, sizeof(*chunk));
+ continue;
+ }
+ if (chunk->addr + chunk->size > memory_end)
+ chunk->size = memory_end - chunk->addr;
+ }
+}
+
+static void __init setup_vmcoreinfo(void)
+{
+#ifdef CONFIG_KEXEC
+ unsigned long ptr = paddr_vmcoreinfo_note();
+
+ copy_to_absolute_zero(&S390_lowcore.vmcore_info, &ptr, sizeof(ptr));
+#endif
+}
+
+#ifdef CONFIG_CRASH_DUMP
+
+/*
+ * Find suitable location for crashkernel memory
+ */
+static unsigned long __init find_crash_base(unsigned long crash_size,
+ char **msg)
+{
+ unsigned long crash_base;
+ struct mem_chunk *chunk;
+ int i;
+
+ if (memory_chunk[0].size < crash_size) {
+ *msg = "first memory chunk must be at least crashkernel size";
+ return 0;
+ }
+ if (OLDMEM_BASE && crash_size == OLDMEM_SIZE)
+ return OLDMEM_BASE;
+
+ for (i = MEMORY_CHUNKS - 1; i >= 0; i--) {
+ chunk = &memory_chunk[i];
+ if (chunk->size == 0)
+ continue;
+ if (chunk->type != CHUNK_READ_WRITE)
+ continue;
+ if (chunk->size < crash_size)
+ continue;
+ crash_base = (chunk->addr + chunk->size) - crash_size;
+ if (crash_base < crash_size)
+ continue;
+ if (crash_base < ZFCPDUMP_HSA_SIZE_MAX)
+ continue;
+ if (crash_base < (unsigned long) INITRD_START + INITRD_SIZE)
+ continue;
+ return crash_base;
+ }
+ *msg = "no suitable area found";
+ return 0;
+}
+
+/*
+ * Check if crash_base and crash_size is valid
+ */
+static int __init verify_crash_base(unsigned long crash_base,
+ unsigned long crash_size,
+ char **msg)
+{
+ struct mem_chunk *chunk;
+ int i;
+
+ /*
+ * Because we do the swap to zero, we must have at least 'crash_size'
+ * bytes free space before crash_base
+ */
+ if (crash_size > crash_base) {
+ *msg = "crashkernel offset must be greater than size";
+ return -EINVAL;
+ }
+
+ /* First memory chunk must be at least crash_size */
+ if (memory_chunk[0].size < crash_size) {
+ *msg = "first memory chunk must be at least crashkernel size";
+ return -EINVAL;
+ }
+ /* Check if we fit into the respective memory chunk */
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ chunk = &memory_chunk[i];
+ if (chunk->size == 0)
+ continue;
+ if (crash_base < chunk->addr)
+ continue;
+ if (crash_base >= chunk->addr + chunk->size)
+ continue;
+ /* we have found the memory chunk */
+ if (crash_base + crash_size > chunk->addr + chunk->size) {
+ *msg = "selected memory chunk is too small for "
+ "crashkernel memory";
+ return -EINVAL;
+ }
+ return 0;
+ }
+ *msg = "invalid memory range specified";
+ return -EINVAL;
+}
+
+/*
+ * Reserve kdump memory by creating a memory hole in the mem_chunk array
+ */
+static void __init reserve_kdump_bootmem(unsigned long addr, unsigned long size,
+ int type)
+{
+ create_mem_hole(memory_chunk, addr, size, type);
+}
+
+/*
+ * When kdump is enabled, we have to ensure that no memory from
+ * the area [0 - crashkernel memory size] and
+ * [crashk_res.start - crashk_res.end] is set offline.
+ */
+static int kdump_mem_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct memory_notify *arg = data;
+
+ if (arg->start_pfn < PFN_DOWN(resource_size(&crashk_res)))
+ return NOTIFY_BAD;
+ if (arg->start_pfn > PFN_DOWN(crashk_res.end))
+ return NOTIFY_OK;
+ if (arg->start_pfn + arg->nr_pages - 1 < PFN_DOWN(crashk_res.start))
+ return NOTIFY_OK;
+ return NOTIFY_BAD;
+}
+
+static struct notifier_block kdump_mem_nb = {
+ .notifier_call = kdump_mem_notifier,
+};
+
+#endif
+
+/*
+ * Make sure that oldmem, where the dump is stored, is protected
+ */
+static void reserve_oldmem(void)
+{
+#ifdef CONFIG_CRASH_DUMP
+ if (!OLDMEM_BASE)
+ return;
+
+ reserve_kdump_bootmem(OLDMEM_BASE, OLDMEM_SIZE, CHUNK_OLDMEM);
+ reserve_kdump_bootmem(OLDMEM_SIZE, memory_end - OLDMEM_SIZE,
+ CHUNK_OLDMEM);
+ if (OLDMEM_BASE + OLDMEM_SIZE == real_memory_size)
+ saved_max_pfn = PFN_DOWN(OLDMEM_BASE) - 1;
+ else
+ saved_max_pfn = PFN_DOWN(real_memory_size) - 1;
+#endif
+}
+
+/*
+ * Reserve memory for kdump kernel to be loaded with kexec
+ */
+static void __init reserve_crashkernel(void)
+{
+#ifdef CONFIG_CRASH_DUMP
+ unsigned long long crash_base, crash_size;
+ char *msg = NULL;
+ int rc;
+
+ rc = parse_crashkernel(boot_command_line, memory_end, &crash_size,
+ &crash_base);
+ if (rc || crash_size == 0)
+ return;
+ crash_base = ALIGN(crash_base, KEXEC_CRASH_MEM_ALIGN);
+ crash_size = ALIGN(crash_size, KEXEC_CRASH_MEM_ALIGN);
+ if (register_memory_notifier(&kdump_mem_nb))
+ return;
+ if (!crash_base)
+ crash_base = find_crash_base(crash_size, &msg);
+ if (!crash_base) {
+ pr_info("crashkernel reservation failed: %s\n", msg);
+ unregister_memory_notifier(&kdump_mem_nb);
+ return;
+ }
+ if (verify_crash_base(crash_base, crash_size, &msg)) {
+ pr_info("crashkernel reservation failed: %s\n", msg);
+ unregister_memory_notifier(&kdump_mem_nb);
+ return;
+ }
+ if (!OLDMEM_BASE && MACHINE_IS_VM)
+ diag10_range(PFN_DOWN(crash_base), PFN_DOWN(crash_size));
+ crashk_res.start = crash_base;
+ crashk_res.end = crash_base + crash_size - 1;
+ insert_resource(&iomem_resource, &crashk_res);
+ reserve_kdump_bootmem(crash_base, crash_size, CHUNK_CRASHK);
+ pr_info("Reserving %lluMB of memory at %lluMB "
+ "for crashkernel (System RAM: %luMB)\n",
+ crash_size >> 20, crash_base >> 20, memory_end >> 20);
+ os_info_crashkernel_add(crash_base, crash_size);
+#endif
+}
+
+static void __init setup_memory(void)
+{
+ unsigned long bootmap_size;
+ unsigned long start_pfn, end_pfn;
+ int i;
+
+ /*
+ * partially used pages are not usable - thus
+ * we are rounding upwards:
+ */
+ start_pfn = PFN_UP(__pa(&_end));
+ end_pfn = max_pfn = PFN_DOWN(memory_end);
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ /*
+ * Move the initrd in case the bitmap of the bootmem allocater
+ * would overwrite it.
+ */
+
+ if (INITRD_START && INITRD_SIZE) {
+ unsigned long bmap_size;
+ unsigned long start;
+
+ bmap_size = bootmem_bootmap_pages(end_pfn - start_pfn + 1);
+ bmap_size = PFN_PHYS(bmap_size);
+
+ if (PFN_PHYS(start_pfn) + bmap_size > INITRD_START) {
+ start = PFN_PHYS(start_pfn) + bmap_size + PAGE_SIZE;
+
+#ifdef CONFIG_CRASH_DUMP
+ if (OLDMEM_BASE) {
+ /* Move initrd behind kdump oldmem */
+ if (start + INITRD_SIZE > OLDMEM_BASE &&
+ start < OLDMEM_BASE + OLDMEM_SIZE)
+ start = OLDMEM_BASE + OLDMEM_SIZE;
+ }
+#endif
+ if (start + INITRD_SIZE > memory_end) {
+ pr_err("initrd extends beyond end of "
+ "memory (0x%08lx > 0x%08lx) "
+ "disabling initrd\n",
+ start + INITRD_SIZE, memory_end);
+ INITRD_START = INITRD_SIZE = 0;
+ } else {
+ pr_info("Moving initrd (0x%08lx -> "
+ "0x%08lx, size: %ld)\n",
+ INITRD_START, start, INITRD_SIZE);
+ memmove((void *) start, (void *) INITRD_START,
+ INITRD_SIZE);
+ INITRD_START = start;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Initialize the boot-time allocator
+ */
+ bootmap_size = init_bootmem(start_pfn, end_pfn);
+
+ /*
+ * Register RAM areas with the bootmem allocator.
+ */
+
+ for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
+ unsigned long start_chunk, end_chunk, pfn;
+
+ if (memory_chunk[i].type != CHUNK_READ_WRITE &&
+ memory_chunk[i].type != CHUNK_CRASHK)
+ continue;
+ start_chunk = PFN_DOWN(memory_chunk[i].addr);
+ end_chunk = start_chunk + PFN_DOWN(memory_chunk[i].size);
+ end_chunk = min(end_chunk, end_pfn);
+ if (start_chunk >= end_chunk)
+ continue;
+ memblock_add_node(PFN_PHYS(start_chunk),
+ PFN_PHYS(end_chunk - start_chunk), 0);
+ pfn = max(start_chunk, start_pfn);
+ for (; pfn < end_chunk; pfn++)
+ page_set_storage_key(PFN_PHYS(pfn),
+ PAGE_DEFAULT_KEY, 0);
+ }
+
+ psw_set_key(PAGE_DEFAULT_KEY);
+
+ free_bootmem_with_active_regions(0, max_pfn);
+
+ /*
+ * Reserve memory used for lowcore/command line/kernel image.
+ */
+ reserve_bootmem(0, (unsigned long)_ehead, BOOTMEM_DEFAULT);
+ reserve_bootmem((unsigned long)_stext,
+ PFN_PHYS(start_pfn) - (unsigned long)_stext,
+ BOOTMEM_DEFAULT);
+ /*
+ * Reserve the bootmem bitmap itself as well. We do this in two
+ * steps (first step was init_bootmem()) because this catches
+ * the (very unlikely) case of us accidentally initializing the
+ * bootmem allocator with an invalid RAM area.
+ */
+ reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size,
+ BOOTMEM_DEFAULT);
+
+#ifdef CONFIG_CRASH_DUMP
+ if (crashk_res.start)
+ reserve_bootmem(crashk_res.start,
+ crashk_res.end - crashk_res.start + 1,
+ BOOTMEM_DEFAULT);
+ if (is_kdump_kernel())
+ reserve_bootmem(elfcorehdr_addr - OLDMEM_BASE,
+ PAGE_ALIGN(elfcorehdr_size), BOOTMEM_DEFAULT);
+#endif
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (INITRD_START && INITRD_SIZE) {
+ if (INITRD_START + INITRD_SIZE <= memory_end) {
+ reserve_bootmem(INITRD_START, INITRD_SIZE,
+ BOOTMEM_DEFAULT);
+ initrd_start = INITRD_START;
+ initrd_end = initrd_start + INITRD_SIZE;
+ } else {
+ pr_err("initrd extends beyond end of "
+ "memory (0x%08lx > 0x%08lx) "
+ "disabling initrd\n",
+ initrd_start + INITRD_SIZE, memory_end);
+ initrd_start = initrd_end = 0;
+ }
+ }
+#endif
+}
+
+/*
+ * Setup hardware capabilities.
+ */
+static void __init setup_hwcaps(void)
+{
+ static const int stfl_bits[6] = { 0, 2, 7, 17, 19, 21 };
+ struct cpuid cpu_id;
+ int i;
+
+ /*
+ * The store facility list bits numbers as found in the principles
+ * of operation are numbered with bit 1UL<<31 as number 0 to
+ * bit 1UL<<0 as number 31.
+ * Bit 0: instructions named N3, "backported" to esa-mode
+ * Bit 2: z/Architecture mode is active
+ * Bit 7: the store-facility-list-extended facility is installed
+ * Bit 17: the message-security assist is installed
+ * Bit 19: the long-displacement facility is installed
+ * Bit 21: the extended-immediate facility is installed
+ * Bit 22: extended-translation facility 3 is installed
+ * Bit 30: extended-translation facility 3 enhancement facility
+ * These get translated to:
+ * HWCAP_S390_ESAN3 bit 0, HWCAP_S390_ZARCH bit 1,
+ * HWCAP_S390_STFLE bit 2, HWCAP_S390_MSA bit 3,
+ * HWCAP_S390_LDISP bit 4, HWCAP_S390_EIMM bit 5 and
+ * HWCAP_S390_ETF3EH bit 8 (22 && 30).
+ */
+ for (i = 0; i < 6; i++)
+ if (test_facility(stfl_bits[i]))
+ elf_hwcap |= 1UL << i;
+
+ if (test_facility(22) && test_facility(30))
+ elf_hwcap |= HWCAP_S390_ETF3EH;
+
+ /*
+ * Check for additional facilities with store-facility-list-extended.
+ * stfle stores doublewords (8 byte) with bit 1ULL<<63 as bit 0
+ * and 1ULL<<0 as bit 63. Bits 0-31 contain the same information
+ * as stored by stfl, bits 32-xxx contain additional facilities.
+ * How many facility words are stored depends on the number of
+ * doublewords passed to the instruction. The additional facilities
+ * are:
+ * Bit 42: decimal floating point facility is installed
+ * Bit 44: perform floating point operation facility is installed
+ * translated to:
+ * HWCAP_S390_DFP bit 6 (42 && 44).
+ */
+ if ((elf_hwcap & (1UL << 2)) && test_facility(42) && test_facility(44))
+ elf_hwcap |= HWCAP_S390_DFP;
+
+ /*
+ * Huge page support HWCAP_S390_HPAGE is bit 7.
+ */
+ if (MACHINE_HAS_HPAGE)
+ elf_hwcap |= HWCAP_S390_HPAGE;
+
+ /*
+ * 64-bit register support for 31-bit processes
+ * HWCAP_S390_HIGH_GPRS is bit 9.
+ */
+ elf_hwcap |= HWCAP_S390_HIGH_GPRS;
+
+ get_cpu_id(&cpu_id);
+ switch (cpu_id.machine) {
+ case 0x9672:
+#if !defined(CONFIG_64BIT)
+ default: /* Use "g5" as default for 31 bit kernels. */
+#endif
+ strcpy(elf_platform, "g5");
+ break;
+ case 0x2064:
+ case 0x2066:
+#if defined(CONFIG_64BIT)
+ default: /* Use "z900" as default for 64 bit kernels. */
+#endif
+ strcpy(elf_platform, "z900");
+ break;
+ case 0x2084:
+ case 0x2086:
+ strcpy(elf_platform, "z990");
+ break;
+ case 0x2094:
+ case 0x2096:
+ strcpy(elf_platform, "z9-109");
+ break;
+ case 0x2097:
+ case 0x2098:
+ strcpy(elf_platform, "z10");
+ break;
+ case 0x2817:
+ case 0x2818:
+ strcpy(elf_platform, "z196");
+ break;
+ }
+}
+
+/*
+ * Setup function called from init/main.c just after the banner
+ * was printed.
+ */
+
+void __init setup_arch(char **cmdline_p)
+{
+ /*
+ * print what head.S has found out about the machine
+ */
+#ifndef CONFIG_64BIT
+ if (MACHINE_IS_VM)
+ pr_info("Linux is running as a z/VM "
+ "guest operating system in 31-bit mode\n");
+ else if (MACHINE_IS_LPAR)
+ pr_info("Linux is running natively in 31-bit mode\n");
+ if (MACHINE_HAS_IEEE)
+ pr_info("The hardware system has IEEE compatible "
+ "floating point units\n");
+ else
+ pr_info("The hardware system has no IEEE compatible "
+ "floating point units\n");
+#else /* CONFIG_64BIT */
+ if (MACHINE_IS_VM)
+ pr_info("Linux is running as a z/VM "
+ "guest operating system in 64-bit mode\n");
+ else if (MACHINE_IS_KVM)
+ pr_info("Linux is running under KVM in 64-bit mode\n");
+ else if (MACHINE_IS_LPAR)
+ pr_info("Linux is running natively in 64-bit mode\n");
+#endif /* CONFIG_64BIT */
+
+ /* Have one command line that is parsed and saved in /proc/cmdline */
+ /* boot_command_line has been already set up in early.c */
+ *cmdline_p = boot_command_line;
+
+ ROOT_DEV = Root_RAM0;
+
+ init_mm.start_code = PAGE_OFFSET;
+ init_mm.end_code = (unsigned long) &_etext;
+ init_mm.end_data = (unsigned long) &_edata;
+ init_mm.brk = (unsigned long) &_end;
+
+ if (MACHINE_HAS_MVCOS)
+ memcpy(&uaccess, &uaccess_mvcos, sizeof(uaccess));
+ else
+ memcpy(&uaccess, &uaccess_std, sizeof(uaccess));
+
+ parse_early_param();
+
+ os_info_init();
+ setup_ipl();
+ setup_memory_end();
+ setup_addressing_mode();
+ reserve_oldmem();
+ reserve_crashkernel();
+ setup_memory();
+ setup_resources();
+ setup_vmcoreinfo();
+ setup_lowcore();
+
+ cpu_init();
+ s390_init_cpu_topology();
+
+ /*
+ * Setup capabilities (ELF_HWCAP & ELF_PLATFORM).
+ */
+ setup_hwcaps();
+
+ /*
+ * Create kernel page tables and switch to virtual addressing.
+ */
+ paging_init();
+
+ /* Setup default console */
+ conmode_default();
+ set_preferred_console();
+
+ /* Setup zfcpdump support */
+ setup_zfcpdump(console_devno);
+}
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
new file mode 100644
index 00000000..f7582b27
--- /dev/null
+++ b/arch/s390/kernel/signal.c
@@ -0,0 +1,522 @@
+/*
+ * arch/s390/kernel/signal.c
+ *
+ * Copyright (C) IBM Corp. 1999,2006
+ * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
+ *
+ * Based on Intel version
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/stddef.h>
+#include <linux/tty.h>
+#include <linux/personality.h>
+#include <linux/binfmts.h>
+#include <linux/tracehook.h>
+#include <linux/syscalls.h>
+#include <linux/compat.h>
+#include <asm/ucontext.h>
+#include <asm/uaccess.h>
+#include <asm/lowcore.h>
+#include <asm/switch_to.h>
+#include "entry.h"
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+
+typedef struct
+{
+ __u8 callee_used_stack[__SIGNAL_FRAMESIZE];
+ struct sigcontext sc;
+ _sigregs sregs;
+ int signo;
+ __u8 retcode[S390_SYSCALL_SIZE];
+} sigframe;
+
+typedef struct
+{
+ __u8 callee_used_stack[__SIGNAL_FRAMESIZE];
+ __u8 retcode[S390_SYSCALL_SIZE];
+ struct siginfo info;
+ struct ucontext uc;
+} rt_sigframe;
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+SYSCALL_DEFINE3(sigsuspend, int, history0, int, history1, old_sigset_t, mask)
+{
+ sigset_t blocked;
+
+ current->saved_sigmask = current->blocked;
+ mask &= _BLOCKABLE;
+ siginitset(&blocked, mask);
+ set_current_blocked(&blocked);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ set_restore_sigmask();
+ return -ERESTARTNOHAND;
+}
+
+SYSCALL_DEFINE3(sigaction, int, sig, const struct old_sigaction __user *, act,
+ struct old_sigaction __user *, oact)
+{
+ struct k_sigaction new_ka, old_ka;
+ int ret;
+
+ if (act) {
+ old_sigset_t mask;
+ if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
+ __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
+ __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) ||
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags) ||
+ __get_user(mask, &act->sa_mask))
+ return -EFAULT;
+ siginitset(&new_ka.sa.sa_mask, mask);
+ }
+
+ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+ if (!ret && oact) {
+ if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
+ __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
+ __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) ||
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags) ||
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask))
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+SYSCALL_DEFINE2(sigaltstack, const stack_t __user *, uss,
+ stack_t __user *, uoss)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ return do_sigaltstack(uss, uoss, regs->gprs[15]);
+}
+
+/* Returns non-zero on fault. */
+static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
+{
+ _sigregs user_sregs;
+
+ save_access_regs(current->thread.acrs);
+
+ /* Copy a 'clean' PSW mask to the user to avoid leaking
+ information about whether PER is currently on. */
+ user_sregs.regs.psw.mask = psw_user_bits |
+ (regs->psw.mask & PSW_MASK_USER);
+ user_sregs.regs.psw.addr = regs->psw.addr;
+ memcpy(&user_sregs.regs.gprs, &regs->gprs, sizeof(sregs->regs.gprs));
+ memcpy(&user_sregs.regs.acrs, current->thread.acrs,
+ sizeof(sregs->regs.acrs));
+ /*
+ * We have to store the fp registers to current->thread.fp_regs
+ * to merge them with the emulated registers.
+ */
+ save_fp_regs(&current->thread.fp_regs);
+ memcpy(&user_sregs.fpregs, &current->thread.fp_regs,
+ sizeof(s390_fp_regs));
+ return __copy_to_user(sregs, &user_sregs, sizeof(_sigregs));
+}
+
+/* Returns positive number on error */
+static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
+{
+ int err;
+ _sigregs user_sregs;
+
+ /* Alwys make any pending restarted system call return -EINTR */
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+ err = __copy_from_user(&user_sregs, sregs, sizeof(_sigregs));
+ if (err)
+ return err;
+ /* Use regs->psw.mask instead of psw_user_bits to preserve PER bit. */
+ regs->psw.mask = (regs->psw.mask & ~PSW_MASK_USER) |
+ (user_sregs.regs.psw.mask & PSW_MASK_USER);
+ /* Check for invalid amode */
+ if (regs->psw.mask & PSW_MASK_EA)
+ regs->psw.mask |= PSW_MASK_BA;
+ regs->psw.addr = user_sregs.regs.psw.addr;
+ memcpy(&regs->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs));
+ memcpy(&current->thread.acrs, &user_sregs.regs.acrs,
+ sizeof(sregs->regs.acrs));
+ restore_access_regs(current->thread.acrs);
+
+ memcpy(&current->thread.fp_regs, &user_sregs.fpregs,
+ sizeof(s390_fp_regs));
+ current->thread.fp_regs.fpc &= FPC_VALID_MASK;
+
+ restore_fp_regs(&current->thread.fp_regs);
+ clear_thread_flag(TIF_SYSCALL); /* No longer in a system call */
+ return 0;
+}
+
+SYSCALL_DEFINE0(sigreturn)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ sigframe __user *frame = (sigframe __user *)regs->gprs[15];
+ sigset_t set;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE))
+ goto badframe;
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ set_current_blocked(&set);
+ if (restore_sigregs(regs, &frame->sregs))
+ goto badframe;
+ return regs->gprs[2];
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+SYSCALL_DEFINE0(rt_sigreturn)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15];
+ sigset_t set;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
+ goto badframe;
+ if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set)))
+ goto badframe;
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ set_current_blocked(&set);
+ if (restore_sigregs(regs, &frame->uc.uc_mcontext))
+ goto badframe;
+ if (do_sigaltstack(&frame->uc.uc_stack, NULL,
+ regs->gprs[15]) == -EFAULT)
+ goto badframe;
+ return regs->gprs[2];
+badframe:
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+/*
+ * Set up a signal frame.
+ */
+
+
+/*
+ * Determine which stack to use..
+ */
+static inline void __user *
+get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
+{
+ unsigned long sp;
+
+ /* Default to using normal stack */
+ sp = regs->gprs[15];
+
+ /* Overflow on alternate signal stack gives SIGSEGV. */
+ if (on_sig_stack(sp) && !on_sig_stack((sp - frame_size) & -8UL))
+ return (void __user *) -1UL;
+
+ /* This is the X/Open sanctioned signal stack switching. */
+ if (ka->sa.sa_flags & SA_ONSTACK) {
+ if (! sas_ss_flags(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+ }
+
+ /* This is the legacy signal stack switching. */
+ else if (!user_mode(regs) &&
+ !(ka->sa.sa_flags & SA_RESTORER) &&
+ ka->sa.sa_restorer) {
+ sp = (unsigned long) ka->sa.sa_restorer;
+ }
+
+ return (void __user *)((sp - frame_size) & -8ul);
+}
+
+static inline int map_signal(int sig)
+{
+ if (current_thread_info()->exec_domain
+ && current_thread_info()->exec_domain->signal_invmap
+ && sig < 32)
+ return current_thread_info()->exec_domain->signal_invmap[sig];
+ else
+ return sig;
+}
+
+static int setup_frame(int sig, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs * regs)
+{
+ sigframe __user *frame;
+
+ frame = get_sigframe(ka, regs, sizeof(sigframe));
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe)))
+ goto give_sigsegv;
+
+ if (frame == (void __user *) -1UL)
+ goto give_sigsegv;
+
+ if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE))
+ goto give_sigsegv;
+
+ if (save_sigregs(regs, &frame->sregs))
+ goto give_sigsegv;
+ if (__put_user(&frame->sregs, &frame->sc.sregs))
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ regs->gprs[14] = (unsigned long)
+ ka->sa.sa_restorer | PSW_ADDR_AMODE;
+ } else {
+ regs->gprs[14] = (unsigned long)
+ frame->retcode | PSW_ADDR_AMODE;
+ if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn,
+ (u16 __user *)(frame->retcode)))
+ goto give_sigsegv;
+ }
+
+ /* Set up backchain. */
+ if (__put_user(regs->gprs[15], (addr_t __user *) frame))
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ regs->gprs[15] = (unsigned long) frame;
+ regs->psw.mask |= PSW_MASK_EA | PSW_MASK_BA; /* 64 bit amode */
+ regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE;
+
+ regs->gprs[2] = map_signal(sig);
+ regs->gprs[3] = (unsigned long) &frame->sc;
+
+ /* We forgot to include these in the sigcontext.
+ To avoid breaking binary compatibility, they are passed as args. */
+ if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
+ sig == SIGTRAP || sig == SIGFPE) {
+ /* set extra registers only for synchronous signals */
+ regs->gprs[4] = regs->int_code & 127;
+ regs->gprs[5] = regs->int_parm_long;
+ regs->gprs[6] = task_thread_info(current)->last_break;
+ }
+
+ /* Place signal number on stack to allow backtrace from handler. */
+ if (__put_user(regs->gprs[2], (int __user *) &frame->signo))
+ goto give_sigsegv;
+ return 0;
+
+give_sigsegv:
+ force_sigsegv(sig, current);
+ return -EFAULT;
+}
+
+static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs * regs)
+{
+ int err = 0;
+ rt_sigframe __user *frame;
+
+ frame = get_sigframe(ka, regs, sizeof(rt_sigframe));
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe)))
+ goto give_sigsegv;
+
+ if (frame == (void __user *) -1UL)
+ goto give_sigsegv;
+
+ if (copy_siginfo_to_user(&frame->info, info))
+ goto give_sigsegv;
+
+ /* Create the ucontext. */
+ err |= __put_user(0, &frame->uc.uc_flags);
+ err |= __put_user(NULL, &frame->uc.uc_link);
+ err |= __put_user((void __user *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
+ err |= __put_user(sas_ss_flags(regs->gprs[15]),
+ &frame->uc.uc_stack.ss_flags);
+ err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
+ err |= save_sigregs(regs, &frame->uc.uc_mcontext);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+ if (err)
+ goto give_sigsegv;
+
+ /* Set up to return from userspace. If provided, use a stub
+ already in userspace. */
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ regs->gprs[14] = (unsigned long)
+ ka->sa.sa_restorer | PSW_ADDR_AMODE;
+ } else {
+ regs->gprs[14] = (unsigned long)
+ frame->retcode | PSW_ADDR_AMODE;
+ if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn,
+ (u16 __user *)(frame->retcode)))
+ goto give_sigsegv;
+ }
+
+ /* Set up backchain. */
+ if (__put_user(regs->gprs[15], (addr_t __user *) frame))
+ goto give_sigsegv;
+
+ /* Set up registers for signal handler */
+ regs->gprs[15] = (unsigned long) frame;
+ regs->psw.mask |= PSW_MASK_EA | PSW_MASK_BA; /* 64 bit amode */
+ regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE;
+
+ regs->gprs[2] = map_signal(sig);
+ regs->gprs[3] = (unsigned long) &frame->info;
+ regs->gprs[4] = (unsigned long) &frame->uc;
+ regs->gprs[5] = task_thread_info(current)->last_break;
+ return 0;
+
+give_sigsegv:
+ force_sigsegv(sig, current);
+ return -EFAULT;
+}
+
+static int handle_signal(unsigned long sig, struct k_sigaction *ka,
+ siginfo_t *info, sigset_t *oldset,
+ struct pt_regs *regs)
+{
+ int ret;
+
+ /* Set up the stack frame */
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ ret = setup_rt_frame(sig, ka, info, oldset, regs);
+ else
+ ret = setup_frame(sig, ka, oldset, regs);
+ if (ret)
+ return ret;
+ block_sigmask(ka, sig);
+ return 0;
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
+ */
+void do_signal(struct pt_regs *regs)
+{
+ siginfo_t info;
+ int signr;
+ struct k_sigaction ka;
+ sigset_t *oldset;
+
+ /*
+ * We want the common case to go fast, which
+ * is why we may in certain cases get here from
+ * kernel mode. Just return without doing anything
+ * if so.
+ */
+ if (!user_mode(regs))
+ return;
+
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ oldset = &current->saved_sigmask;
+ else
+ oldset = &current->blocked;
+
+ /*
+ * Get signal to deliver. When running under ptrace, at this point
+ * the debugger may change all our registers, including the system
+ * call information.
+ */
+ current_thread_info()->system_call =
+ test_thread_flag(TIF_SYSCALL) ? regs->int_code : 0;
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+
+ if (signr > 0) {
+ /* Whee! Actually deliver the signal. */
+ if (current_thread_info()->system_call) {
+ regs->int_code = current_thread_info()->system_call;
+ /* Check for system call restarting. */
+ switch (regs->gprs[2]) {
+ case -ERESTART_RESTARTBLOCK:
+ case -ERESTARTNOHAND:
+ regs->gprs[2] = -EINTR;
+ break;
+ case -ERESTARTSYS:
+ if (!(ka.sa.sa_flags & SA_RESTART)) {
+ regs->gprs[2] = -EINTR;
+ break;
+ }
+ /* fallthrough */
+ case -ERESTARTNOINTR:
+ regs->gprs[2] = regs->orig_gpr2;
+ regs->psw.addr =
+ __rewind_psw(regs->psw,
+ regs->int_code >> 16);
+ break;
+ }
+ }
+ /* No longer in a system call */
+ clear_thread_flag(TIF_SYSCALL);
+
+ if ((is_compat_task() ?
+ handle_signal32(signr, &ka, &info, oldset, regs) :
+ handle_signal(signr, &ka, &info, oldset, regs)) == 0) {
+ /*
+ * A signal was successfully delivered; the saved
+ * sigmask will have been stored in the signal frame,
+ * and will be restored by sigreturn, so we can simply
+ * clear the TIF_RESTORE_SIGMASK flag.
+ */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK))
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+
+ /*
+ * Let tracing know that we've done the handler setup.
+ */
+ tracehook_signal_handler(signr, &info, &ka, regs,
+ test_thread_flag(TIF_SINGLE_STEP));
+ }
+ return;
+ }
+
+ /* No handlers present - check for system call restart */
+ clear_thread_flag(TIF_SYSCALL);
+ if (current_thread_info()->system_call) {
+ regs->int_code = current_thread_info()->system_call;
+ switch (regs->gprs[2]) {
+ case -ERESTART_RESTARTBLOCK:
+ /* Restart with sys_restart_syscall */
+ regs->int_code = __NR_restart_syscall;
+ /* fallthrough */
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ /* Restart system call with magic TIF bit. */
+ regs->gprs[2] = regs->orig_gpr2;
+ set_thread_flag(TIF_SYSCALL);
+ break;
+ }
+ }
+
+ /*
+ * If there's no signal to deliver, we just put the saved sigmask back.
+ */
+ if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
+ clear_thread_flag(TIF_RESTORE_SIGMASK);
+ sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
+ }
+}
+
+void do_notify_resume(struct pt_regs *regs)
+{
+ clear_thread_flag(TIF_NOTIFY_RESUME);
+ tracehook_notify_resume(regs);
+ if (current->replacement_session_keyring)
+ key_replace_session_keyring();
+}
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
new file mode 100644
index 00000000..1f772276
--- /dev/null
+++ b/arch/s390/kernel/smp.c
@@ -0,0 +1,1146 @@
+/*
+ * SMP related functions
+ *
+ * Copyright IBM Corp. 1999,2012
+ * Author(s): Denis Joseph Barrow,
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>,
+ * Heiko Carstens <heiko.carstens@de.ibm.com>,
+ *
+ * based on other smp stuff by
+ * (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net>
+ * (c) 1998 Ingo Molnar
+ *
+ * The code outside of smp.c uses logical cpu numbers, only smp.c does
+ * the translation of logical to physical cpu ids. All new code that
+ * operates on physical cpu numbers needs to go into smp.c.
+ */
+
+#define KMSG_COMPONENT "cpu"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irqflags.h>
+#include <linux/cpu.h>
+#include <linux/slab.h>
+#include <linux/crash_dump.h>
+#include <asm/asm-offsets.h>
+#include <asm/switch_to.h>
+#include <asm/facility.h>
+#include <asm/ipl.h>
+#include <asm/setup.h>
+#include <asm/irq.h>
+#include <asm/tlbflush.h>
+#include <asm/timer.h>
+#include <asm/lowcore.h>
+#include <asm/sclp.h>
+#include <asm/vdso.h>
+#include <asm/debug.h>
+#include <asm/os_info.h>
+#include "entry.h"
+
+enum {
+ sigp_sense = 1,
+ sigp_external_call = 2,
+ sigp_emergency_signal = 3,
+ sigp_start = 4,
+ sigp_stop = 5,
+ sigp_restart = 6,
+ sigp_stop_and_store_status = 9,
+ sigp_initial_cpu_reset = 11,
+ sigp_cpu_reset = 12,
+ sigp_set_prefix = 13,
+ sigp_store_status_at_address = 14,
+ sigp_store_extended_status_at_address = 15,
+ sigp_set_architecture = 18,
+ sigp_conditional_emergency_signal = 19,
+ sigp_sense_running = 21,
+};
+
+enum {
+ sigp_order_code_accepted = 0,
+ sigp_status_stored = 1,
+ sigp_busy = 2,
+ sigp_not_operational = 3,
+};
+
+enum {
+ ec_schedule = 0,
+ ec_call_function,
+ ec_call_function_single,
+ ec_stop_cpu,
+};
+
+enum {
+ CPU_STATE_STANDBY,
+ CPU_STATE_CONFIGURED,
+};
+
+struct pcpu {
+ struct cpu cpu;
+ struct task_struct *idle; /* idle process for the cpu */
+ struct _lowcore *lowcore; /* lowcore page(s) for the cpu */
+ unsigned long async_stack; /* async stack for the cpu */
+ unsigned long panic_stack; /* panic stack for the cpu */
+ unsigned long ec_mask; /* bit mask for ec_xxx functions */
+ int state; /* physical cpu state */
+ u32 status; /* last status received via sigp */
+ u16 address; /* physical cpu address */
+};
+
+static u8 boot_cpu_type;
+static u16 boot_cpu_address;
+static struct pcpu pcpu_devices[NR_CPUS];
+
+DEFINE_MUTEX(smp_cpu_state_mutex);
+
+/*
+ * Signal processor helper functions.
+ */
+static inline int __pcpu_sigp(u16 addr, u8 order, u32 parm, u32 *status)
+{
+ register unsigned int reg1 asm ("1") = parm;
+ int cc;
+
+ asm volatile(
+ " sigp %1,%2,0(%3)\n"
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (cc), "+d" (reg1) : "d" (addr), "a" (order) : "cc");
+ if (status && cc == 1)
+ *status = reg1;
+ return cc;
+}
+
+static inline int __pcpu_sigp_relax(u16 addr, u8 order, u32 parm, u32 *status)
+{
+ int cc;
+
+ while (1) {
+ cc = __pcpu_sigp(addr, order, parm, status);
+ if (cc != sigp_busy)
+ return cc;
+ cpu_relax();
+ }
+}
+
+static int pcpu_sigp_retry(struct pcpu *pcpu, u8 order, u32 parm)
+{
+ int cc, retry;
+
+ for (retry = 0; ; retry++) {
+ cc = __pcpu_sigp(pcpu->address, order, parm, &pcpu->status);
+ if (cc != sigp_busy)
+ break;
+ if (retry >= 3)
+ udelay(10);
+ }
+ return cc;
+}
+
+static inline int pcpu_stopped(struct pcpu *pcpu)
+{
+ if (__pcpu_sigp(pcpu->address, sigp_sense,
+ 0, &pcpu->status) != sigp_status_stored)
+ return 0;
+ /* Check for stopped and check stop state */
+ return !!(pcpu->status & 0x50);
+}
+
+static inline int pcpu_running(struct pcpu *pcpu)
+{
+ if (__pcpu_sigp(pcpu->address, sigp_sense_running,
+ 0, &pcpu->status) != sigp_status_stored)
+ return 1;
+ /* Check for running status */
+ return !(pcpu->status & 0x400);
+}
+
+/*
+ * Find struct pcpu by cpu address.
+ */
+static struct pcpu *pcpu_find_address(const struct cpumask *mask, int address)
+{
+ int cpu;
+
+ for_each_cpu(cpu, mask)
+ if (pcpu_devices[cpu].address == address)
+ return pcpu_devices + cpu;
+ return NULL;
+}
+
+static void pcpu_ec_call(struct pcpu *pcpu, int ec_bit)
+{
+ int order;
+
+ set_bit(ec_bit, &pcpu->ec_mask);
+ order = pcpu_running(pcpu) ?
+ sigp_external_call : sigp_emergency_signal;
+ pcpu_sigp_retry(pcpu, order, 0);
+}
+
+static int __cpuinit pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
+{
+ struct _lowcore *lc;
+
+ if (pcpu != &pcpu_devices[0]) {
+ pcpu->lowcore = (struct _lowcore *)
+ __get_free_pages(GFP_KERNEL | GFP_DMA, LC_ORDER);
+ pcpu->async_stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
+ pcpu->panic_stack = __get_free_page(GFP_KERNEL);
+ if (!pcpu->lowcore || !pcpu->panic_stack || !pcpu->async_stack)
+ goto out;
+ }
+ lc = pcpu->lowcore;
+ memcpy(lc, &S390_lowcore, 512);
+ memset((char *) lc + 512, 0, sizeof(*lc) - 512);
+ lc->async_stack = pcpu->async_stack + ASYNC_SIZE;
+ lc->panic_stack = pcpu->panic_stack + PAGE_SIZE;
+ lc->cpu_nr = cpu;
+#ifndef CONFIG_64BIT
+ if (MACHINE_HAS_IEEE) {
+ lc->extended_save_area_addr = get_zeroed_page(GFP_KERNEL);
+ if (!lc->extended_save_area_addr)
+ goto out;
+ }
+#else
+ if (vdso_alloc_per_cpu(lc))
+ goto out;
+#endif
+ lowcore_ptr[cpu] = lc;
+ pcpu_sigp_retry(pcpu, sigp_set_prefix, (u32)(unsigned long) lc);
+ return 0;
+out:
+ if (pcpu != &pcpu_devices[0]) {
+ free_page(pcpu->panic_stack);
+ free_pages(pcpu->async_stack, ASYNC_ORDER);
+ free_pages((unsigned long) pcpu->lowcore, LC_ORDER);
+ }
+ return -ENOMEM;
+}
+
+static void pcpu_free_lowcore(struct pcpu *pcpu)
+{
+ pcpu_sigp_retry(pcpu, sigp_set_prefix, 0);
+ lowcore_ptr[pcpu - pcpu_devices] = NULL;
+#ifndef CONFIG_64BIT
+ if (MACHINE_HAS_IEEE) {
+ struct _lowcore *lc = pcpu->lowcore;
+
+ free_page((unsigned long) lc->extended_save_area_addr);
+ lc->extended_save_area_addr = 0;
+ }
+#else
+ vdso_free_per_cpu(pcpu->lowcore);
+#endif
+ if (pcpu != &pcpu_devices[0]) {
+ free_page(pcpu->panic_stack);
+ free_pages(pcpu->async_stack, ASYNC_ORDER);
+ free_pages((unsigned long) pcpu->lowcore, LC_ORDER);
+ }
+}
+
+static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
+{
+ struct _lowcore *lc = pcpu->lowcore;
+
+ atomic_inc(&init_mm.context.attach_count);
+ lc->cpu_nr = cpu;
+ lc->percpu_offset = __per_cpu_offset[cpu];
+ lc->kernel_asce = S390_lowcore.kernel_asce;
+ lc->machine_flags = S390_lowcore.machine_flags;
+ lc->ftrace_func = S390_lowcore.ftrace_func;
+ lc->user_timer = lc->system_timer = lc->steal_timer = 0;
+ __ctl_store(lc->cregs_save_area, 0, 15);
+ save_access_regs((unsigned int *) lc->access_regs_save_area);
+ memcpy(lc->stfle_fac_list, S390_lowcore.stfle_fac_list,
+ MAX_FACILITY_BIT/8);
+}
+
+static void pcpu_attach_task(struct pcpu *pcpu, struct task_struct *tsk)
+{
+ struct _lowcore *lc = pcpu->lowcore;
+ struct thread_info *ti = task_thread_info(tsk);
+
+ lc->kernel_stack = (unsigned long) task_stack_page(tsk) + THREAD_SIZE;
+ lc->thread_info = (unsigned long) task_thread_info(tsk);
+ lc->current_task = (unsigned long) tsk;
+ lc->user_timer = ti->user_timer;
+ lc->system_timer = ti->system_timer;
+ lc->steal_timer = 0;
+}
+
+static void pcpu_start_fn(struct pcpu *pcpu, void (*func)(void *), void *data)
+{
+ struct _lowcore *lc = pcpu->lowcore;
+
+ lc->restart_stack = lc->kernel_stack;
+ lc->restart_fn = (unsigned long) func;
+ lc->restart_data = (unsigned long) data;
+ lc->restart_source = -1UL;
+ pcpu_sigp_retry(pcpu, sigp_restart, 0);
+}
+
+/*
+ * Call function via PSW restart on pcpu and stop the current cpu.
+ */
+static void pcpu_delegate(struct pcpu *pcpu, void (*func)(void *),
+ void *data, unsigned long stack)
+{
+ struct _lowcore *lc = pcpu->lowcore;
+ unsigned short this_cpu;
+
+ __load_psw_mask(psw_kernel_bits);
+ this_cpu = stap();
+ if (pcpu->address == this_cpu)
+ func(data); /* should not return */
+ /* Stop target cpu (if func returns this stops the current cpu). */
+ pcpu_sigp_retry(pcpu, sigp_stop, 0);
+ /* Restart func on the target cpu and stop the current cpu. */
+ lc->restart_stack = stack;
+ lc->restart_fn = (unsigned long) func;
+ lc->restart_data = (unsigned long) data;
+ lc->restart_source = (unsigned long) this_cpu;
+ asm volatile(
+ "0: sigp 0,%0,6 # sigp restart to target cpu\n"
+ " brc 2,0b # busy, try again\n"
+ "1: sigp 0,%1,5 # sigp stop to current cpu\n"
+ " brc 2,1b # busy, try again\n"
+ : : "d" (pcpu->address), "d" (this_cpu) : "0", "1", "cc");
+ for (;;) ;
+}
+
+/*
+ * Call function on an online CPU.
+ */
+void smp_call_online_cpu(void (*func)(void *), void *data)
+{
+ struct pcpu *pcpu;
+
+ /* Use the current cpu if it is online. */
+ pcpu = pcpu_find_address(cpu_online_mask, stap());
+ if (!pcpu)
+ /* Use the first online cpu. */
+ pcpu = pcpu_devices + cpumask_first(cpu_online_mask);
+ pcpu_delegate(pcpu, func, data, (unsigned long) restart_stack);
+}
+
+/*
+ * Call function on the ipl CPU.
+ */
+void smp_call_ipl_cpu(void (*func)(void *), void *data)
+{
+ pcpu_delegate(&pcpu_devices[0], func, data,
+ pcpu_devices->panic_stack + PAGE_SIZE);
+}
+
+int smp_find_processor_id(u16 address)
+{
+ int cpu;
+
+ for_each_present_cpu(cpu)
+ if (pcpu_devices[cpu].address == address)
+ return cpu;
+ return -1;
+}
+
+int smp_vcpu_scheduled(int cpu)
+{
+ return pcpu_running(pcpu_devices + cpu);
+}
+
+void smp_yield(void)
+{
+ if (MACHINE_HAS_DIAG44)
+ asm volatile("diag 0,0,0x44");
+}
+
+void smp_yield_cpu(int cpu)
+{
+ if (MACHINE_HAS_DIAG9C)
+ asm volatile("diag %0,0,0x9c"
+ : : "d" (pcpu_devices[cpu].address));
+ else if (MACHINE_HAS_DIAG44)
+ asm volatile("diag 0,0,0x44");
+}
+
+/*
+ * Send cpus emergency shutdown signal. This gives the cpus the
+ * opportunity to complete outstanding interrupts.
+ */
+void smp_emergency_stop(cpumask_t *cpumask)
+{
+ u64 end;
+ int cpu;
+
+ end = get_clock() + (1000000UL << 12);
+ for_each_cpu(cpu, cpumask) {
+ struct pcpu *pcpu = pcpu_devices + cpu;
+ set_bit(ec_stop_cpu, &pcpu->ec_mask);
+ while (__pcpu_sigp(pcpu->address, sigp_emergency_signal,
+ 0, NULL) == sigp_busy &&
+ get_clock() < end)
+ cpu_relax();
+ }
+ while (get_clock() < end) {
+ for_each_cpu(cpu, cpumask)
+ if (pcpu_stopped(pcpu_devices + cpu))
+ cpumask_clear_cpu(cpu, cpumask);
+ if (cpumask_empty(cpumask))
+ break;
+ cpu_relax();
+ }
+}
+
+/*
+ * Stop all cpus but the current one.
+ */
+void smp_send_stop(void)
+{
+ cpumask_t cpumask;
+ int cpu;
+
+ /* Disable all interrupts/machine checks */
+ __load_psw_mask(psw_kernel_bits | PSW_MASK_DAT);
+ trace_hardirqs_off();
+
+ debug_set_critical();
+ cpumask_copy(&cpumask, cpu_online_mask);
+ cpumask_clear_cpu(smp_processor_id(), &cpumask);
+
+ if (oops_in_progress)
+ smp_emergency_stop(&cpumask);
+
+ /* stop all processors */
+ for_each_cpu(cpu, &cpumask) {
+ struct pcpu *pcpu = pcpu_devices + cpu;
+ pcpu_sigp_retry(pcpu, sigp_stop, 0);
+ while (!pcpu_stopped(pcpu))
+ cpu_relax();
+ }
+}
+
+/*
+ * Stop the current cpu.
+ */
+void smp_stop_cpu(void)
+{
+ pcpu_sigp_retry(pcpu_devices + smp_processor_id(), sigp_stop, 0);
+ for (;;) ;
+}
+
+/*
+ * This is the main routine where commands issued by other
+ * cpus are handled.
+ */
+static void do_ext_call_interrupt(struct ext_code ext_code,
+ unsigned int param32, unsigned long param64)
+{
+ unsigned long bits;
+ int cpu;
+
+ cpu = smp_processor_id();
+ if (ext_code.code == 0x1202)
+ kstat_cpu(cpu).irqs[EXTINT_EXC]++;
+ else
+ kstat_cpu(cpu).irqs[EXTINT_EMS]++;
+ /*
+ * handle bit signal external calls
+ */
+ bits = xchg(&pcpu_devices[cpu].ec_mask, 0);
+
+ if (test_bit(ec_stop_cpu, &bits))
+ smp_stop_cpu();
+
+ if (test_bit(ec_schedule, &bits))
+ scheduler_ipi();
+
+ if (test_bit(ec_call_function, &bits))
+ generic_smp_call_function_interrupt();
+
+ if (test_bit(ec_call_function_single, &bits))
+ generic_smp_call_function_single_interrupt();
+
+}
+
+void arch_send_call_function_ipi_mask(const struct cpumask *mask)
+{
+ int cpu;
+
+ for_each_cpu(cpu, mask)
+ pcpu_ec_call(pcpu_devices + cpu, ec_call_function);
+}
+
+void arch_send_call_function_single_ipi(int cpu)
+{
+ pcpu_ec_call(pcpu_devices + cpu, ec_call_function_single);
+}
+
+#ifndef CONFIG_64BIT
+/*
+ * this function sends a 'purge tlb' signal to another CPU.
+ */
+static void smp_ptlb_callback(void *info)
+{
+ __tlb_flush_local();
+}
+
+void smp_ptlb_all(void)
+{
+ on_each_cpu(smp_ptlb_callback, NULL, 1);
+}
+EXPORT_SYMBOL(smp_ptlb_all);
+#endif /* ! CONFIG_64BIT */
+
+/*
+ * this function sends a 'reschedule' IPI to another CPU.
+ * it goes straight through and wastes no time serializing
+ * anything. Worst case is that we lose a reschedule ...
+ */
+void smp_send_reschedule(int cpu)
+{
+ pcpu_ec_call(pcpu_devices + cpu, ec_schedule);
+}
+
+/*
+ * parameter area for the set/clear control bit callbacks
+ */
+struct ec_creg_mask_parms {
+ unsigned long orval;
+ unsigned long andval;
+ int cr;
+};
+
+/*
+ * callback for setting/clearing control bits
+ */
+static void smp_ctl_bit_callback(void *info)
+{
+ struct ec_creg_mask_parms *pp = info;
+ unsigned long cregs[16];
+
+ __ctl_store(cregs, 0, 15);
+ cregs[pp->cr] = (cregs[pp->cr] & pp->andval) | pp->orval;
+ __ctl_load(cregs, 0, 15);
+}
+
+/*
+ * Set a bit in a control register of all cpus
+ */
+void smp_ctl_set_bit(int cr, int bit)
+{
+ struct ec_creg_mask_parms parms = { 1UL << bit, -1UL, cr };
+
+ on_each_cpu(smp_ctl_bit_callback, &parms, 1);
+}
+EXPORT_SYMBOL(smp_ctl_set_bit);
+
+/*
+ * Clear a bit in a control register of all cpus
+ */
+void smp_ctl_clear_bit(int cr, int bit)
+{
+ struct ec_creg_mask_parms parms = { 0, ~(1UL << bit), cr };
+
+ on_each_cpu(smp_ctl_bit_callback, &parms, 1);
+}
+EXPORT_SYMBOL(smp_ctl_clear_bit);
+
+#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_CRASH_DUMP)
+
+struct save_area *zfcpdump_save_areas[NR_CPUS + 1];
+EXPORT_SYMBOL_GPL(zfcpdump_save_areas);
+
+static void __init smp_get_save_area(int cpu, u16 address)
+{
+ void *lc = pcpu_devices[0].lowcore;
+ struct save_area *save_area;
+
+ if (is_kdump_kernel())
+ return;
+ if (!OLDMEM_BASE && (address == boot_cpu_address ||
+ ipl_info.type != IPL_TYPE_FCP_DUMP))
+ return;
+ if (cpu >= NR_CPUS) {
+ pr_warning("CPU %i exceeds the maximum %i and is excluded "
+ "from the dump\n", cpu, NR_CPUS - 1);
+ return;
+ }
+ save_area = kmalloc(sizeof(struct save_area), GFP_KERNEL);
+ if (!save_area)
+ panic("could not allocate memory for save area\n");
+ zfcpdump_save_areas[cpu] = save_area;
+#ifdef CONFIG_CRASH_DUMP
+ if (address == boot_cpu_address) {
+ /* Copy the registers of the boot cpu. */
+ copy_oldmem_page(1, (void *) save_area, sizeof(*save_area),
+ SAVE_AREA_BASE - PAGE_SIZE, 0);
+ return;
+ }
+#endif
+ /* Get the registers of a non-boot cpu. */
+ __pcpu_sigp_relax(address, sigp_stop_and_store_status, 0, NULL);
+ memcpy_real(save_area, lc + SAVE_AREA_BASE, sizeof(*save_area));
+}
+
+int smp_store_status(int cpu)
+{
+ struct pcpu *pcpu;
+
+ pcpu = pcpu_devices + cpu;
+ if (__pcpu_sigp_relax(pcpu->address, sigp_stop_and_store_status,
+ 0, NULL) != sigp_order_code_accepted)
+ return -EIO;
+ return 0;
+}
+
+#else /* CONFIG_ZFCPDUMP || CONFIG_CRASH_DUMP */
+
+static inline void smp_get_save_area(int cpu, u16 address) { }
+
+#endif /* CONFIG_ZFCPDUMP || CONFIG_CRASH_DUMP */
+
+static struct sclp_cpu_info *smp_get_cpu_info(void)
+{
+ static int use_sigp_detection;
+ struct sclp_cpu_info *info;
+ int address;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (info && (use_sigp_detection || sclp_get_cpu_info(info))) {
+ use_sigp_detection = 1;
+ for (address = 0; address <= MAX_CPU_ADDRESS; address++) {
+ if (__pcpu_sigp_relax(address, sigp_sense, 0, NULL) ==
+ sigp_not_operational)
+ continue;
+ info->cpu[info->configured].address = address;
+ info->configured++;
+ }
+ info->combined = info->configured;
+ }
+ return info;
+}
+
+static int __devinit smp_add_present_cpu(int cpu);
+
+static int __devinit __smp_rescan_cpus(struct sclp_cpu_info *info,
+ int sysfs_add)
+{
+ struct pcpu *pcpu;
+ cpumask_t avail;
+ int cpu, nr, i;
+
+ nr = 0;
+ cpumask_xor(&avail, cpu_possible_mask, cpu_present_mask);
+ cpu = cpumask_first(&avail);
+ for (i = 0; (i < info->combined) && (cpu < nr_cpu_ids); i++) {
+ if (info->has_cpu_type && info->cpu[i].type != boot_cpu_type)
+ continue;
+ if (pcpu_find_address(cpu_present_mask, info->cpu[i].address))
+ continue;
+ pcpu = pcpu_devices + cpu;
+ pcpu->address = info->cpu[i].address;
+ pcpu->state = (cpu >= info->configured) ?
+ CPU_STATE_STANDBY : CPU_STATE_CONFIGURED;
+ cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
+ set_cpu_present(cpu, true);
+ if (sysfs_add && smp_add_present_cpu(cpu) != 0)
+ set_cpu_present(cpu, false);
+ else
+ nr++;
+ cpu = cpumask_next(cpu, &avail);
+ }
+ return nr;
+}
+
+static void __init smp_detect_cpus(void)
+{
+ unsigned int cpu, c_cpus, s_cpus;
+ struct sclp_cpu_info *info;
+
+ info = smp_get_cpu_info();
+ if (!info)
+ panic("smp_detect_cpus failed to allocate memory\n");
+ if (info->has_cpu_type) {
+ for (cpu = 0; cpu < info->combined; cpu++) {
+ if (info->cpu[cpu].address != boot_cpu_address)
+ continue;
+ /* The boot cpu dictates the cpu type. */
+ boot_cpu_type = info->cpu[cpu].type;
+ break;
+ }
+ }
+ c_cpus = s_cpus = 0;
+ for (cpu = 0; cpu < info->combined; cpu++) {
+ if (info->has_cpu_type && info->cpu[cpu].type != boot_cpu_type)
+ continue;
+ if (cpu < info->configured) {
+ smp_get_save_area(c_cpus, info->cpu[cpu].address);
+ c_cpus++;
+ } else
+ s_cpus++;
+ }
+ pr_info("%d configured CPUs, %d standby CPUs\n", c_cpus, s_cpus);
+ get_online_cpus();
+ __smp_rescan_cpus(info, 0);
+ put_online_cpus();
+ kfree(info);
+}
+
+/*
+ * Activate a secondary processor.
+ */
+static void __cpuinit smp_start_secondary(void *cpuvoid)
+{
+ S390_lowcore.last_update_clock = get_clock();
+ S390_lowcore.restart_stack = (unsigned long) restart_stack;
+ S390_lowcore.restart_fn = (unsigned long) do_restart;
+ S390_lowcore.restart_data = 0;
+ S390_lowcore.restart_source = -1UL;
+ restore_access_regs(S390_lowcore.access_regs_save_area);
+ __ctl_load(S390_lowcore.cregs_save_area, 0, 15);
+ __load_psw_mask(psw_kernel_bits | PSW_MASK_DAT);
+ cpu_init();
+ preempt_disable();
+ init_cpu_timer();
+ init_cpu_vtimer();
+ pfault_init();
+ notify_cpu_starting(smp_processor_id());
+ ipi_call_lock();
+ set_cpu_online(smp_processor_id(), true);
+ ipi_call_unlock();
+ local_irq_enable();
+ /* cpu_idle will call schedule for us */
+ cpu_idle();
+}
+
+struct create_idle {
+ struct work_struct work;
+ struct task_struct *idle;
+ struct completion done;
+ int cpu;
+};
+
+static void __cpuinit smp_fork_idle(struct work_struct *work)
+{
+ struct create_idle *c_idle;
+
+ c_idle = container_of(work, struct create_idle, work);
+ c_idle->idle = fork_idle(c_idle->cpu);
+ complete(&c_idle->done);
+}
+
+/* Upping and downing of CPUs */
+int __cpuinit __cpu_up(unsigned int cpu)
+{
+ struct create_idle c_idle;
+ struct pcpu *pcpu;
+ int rc;
+
+ pcpu = pcpu_devices + cpu;
+ if (pcpu->state != CPU_STATE_CONFIGURED)
+ return -EIO;
+ if (pcpu_sigp_retry(pcpu, sigp_initial_cpu_reset, 0) !=
+ sigp_order_code_accepted)
+ return -EIO;
+ if (!pcpu->idle) {
+ c_idle.done = COMPLETION_INITIALIZER_ONSTACK(c_idle.done);
+ INIT_WORK_ONSTACK(&c_idle.work, smp_fork_idle);
+ c_idle.cpu = cpu;
+ schedule_work(&c_idle.work);
+ wait_for_completion(&c_idle.done);
+ if (IS_ERR(c_idle.idle))
+ return PTR_ERR(c_idle.idle);
+ pcpu->idle = c_idle.idle;
+ }
+ init_idle(pcpu->idle, cpu);
+ rc = pcpu_alloc_lowcore(pcpu, cpu);
+ if (rc)
+ return rc;
+ pcpu_prepare_secondary(pcpu, cpu);
+ pcpu_attach_task(pcpu, pcpu->idle);
+ pcpu_start_fn(pcpu, smp_start_secondary, NULL);
+ while (!cpu_online(cpu))
+ cpu_relax();
+ return 0;
+}
+
+static int __init setup_possible_cpus(char *s)
+{
+ int max, cpu;
+
+ if (kstrtoint(s, 0, &max) < 0)
+ return 0;
+ init_cpu_possible(cpumask_of(0));
+ for (cpu = 1; cpu < max && cpu < nr_cpu_ids; cpu++)
+ set_cpu_possible(cpu, true);
+ return 0;
+}
+early_param("possible_cpus", setup_possible_cpus);
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+int __cpu_disable(void)
+{
+ unsigned long cregs[16];
+
+ set_cpu_online(smp_processor_id(), false);
+ /* Disable pseudo page faults on this cpu. */
+ pfault_fini();
+ /* Disable interrupt sources via control register. */
+ __ctl_store(cregs, 0, 15);
+ cregs[0] &= ~0x0000ee70UL; /* disable all external interrupts */
+ cregs[6] &= ~0xff000000UL; /* disable all I/O interrupts */
+ cregs[14] &= ~0x1f000000UL; /* disable most machine checks */
+ __ctl_load(cregs, 0, 15);
+ return 0;
+}
+
+void __cpu_die(unsigned int cpu)
+{
+ struct pcpu *pcpu;
+
+ /* Wait until target cpu is down */
+ pcpu = pcpu_devices + cpu;
+ while (!pcpu_stopped(pcpu))
+ cpu_relax();
+ pcpu_free_lowcore(pcpu);
+ atomic_dec(&init_mm.context.attach_count);
+}
+
+void __noreturn cpu_die(void)
+{
+ idle_task_exit();
+ pcpu_sigp_retry(pcpu_devices + smp_processor_id(), sigp_stop, 0);
+ for (;;) ;
+}
+
+#endif /* CONFIG_HOTPLUG_CPU */
+
+static void smp_call_os_info_init_fn(void)
+{
+ int (*init_fn)(void);
+ unsigned long size;
+
+ init_fn = os_info_old_entry(OS_INFO_INIT_FN, &size);
+ if (!init_fn)
+ return;
+ init_fn();
+}
+
+void __init smp_prepare_cpus(unsigned int max_cpus)
+{
+ /* request the 0x1201 emergency signal external interrupt */
+ if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0)
+ panic("Couldn't request external interrupt 0x1201");
+ /* request the 0x1202 external call external interrupt */
+ if (register_external_interrupt(0x1202, do_ext_call_interrupt) != 0)
+ panic("Couldn't request external interrupt 0x1202");
+ smp_call_os_info_init_fn();
+ smp_detect_cpus();
+}
+
+void __init smp_prepare_boot_cpu(void)
+{
+ struct pcpu *pcpu = pcpu_devices;
+
+ boot_cpu_address = stap();
+ pcpu->idle = current;
+ pcpu->state = CPU_STATE_CONFIGURED;
+ pcpu->address = boot_cpu_address;
+ pcpu->lowcore = (struct _lowcore *)(unsigned long) store_prefix();
+ pcpu->async_stack = S390_lowcore.async_stack - ASYNC_SIZE;
+ pcpu->panic_stack = S390_lowcore.panic_stack - PAGE_SIZE;
+ S390_lowcore.percpu_offset = __per_cpu_offset[0];
+ cpu_set_polarization(0, POLARIZATION_UNKNOWN);
+ set_cpu_present(0, true);
+ set_cpu_online(0, true);
+}
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+}
+
+void __init smp_setup_processor_id(void)
+{
+ S390_lowcore.cpu_nr = 0;
+}
+
+/*
+ * the frequency of the profiling timer can be changed
+ * by writing a multiplier value into /proc/profile.
+ *
+ * usually you want to run this on all CPUs ;)
+ */
+int setup_profiling_timer(unsigned int multiplier)
+{
+ return 0;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static ssize_t cpu_configure_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t count;
+
+ mutex_lock(&smp_cpu_state_mutex);
+ count = sprintf(buf, "%d\n", pcpu_devices[dev->id].state);
+ mutex_unlock(&smp_cpu_state_mutex);
+ return count;
+}
+
+static ssize_t cpu_configure_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pcpu *pcpu;
+ int cpu, val, rc;
+ char delim;
+
+ if (sscanf(buf, "%d %c", &val, &delim) != 1)
+ return -EINVAL;
+ if (val != 0 && val != 1)
+ return -EINVAL;
+ get_online_cpus();
+ mutex_lock(&smp_cpu_state_mutex);
+ rc = -EBUSY;
+ /* disallow configuration changes of online cpus and cpu 0 */
+ cpu = dev->id;
+ if (cpu_online(cpu) || cpu == 0)
+ goto out;
+ pcpu = pcpu_devices + cpu;
+ rc = 0;
+ switch (val) {
+ case 0:
+ if (pcpu->state != CPU_STATE_CONFIGURED)
+ break;
+ rc = sclp_cpu_deconfigure(pcpu->address);
+ if (rc)
+ break;
+ pcpu->state = CPU_STATE_STANDBY;
+ cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
+ topology_expect_change();
+ break;
+ case 1:
+ if (pcpu->state != CPU_STATE_STANDBY)
+ break;
+ rc = sclp_cpu_configure(pcpu->address);
+ if (rc)
+ break;
+ pcpu->state = CPU_STATE_CONFIGURED;
+ cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
+ topology_expect_change();
+ break;
+ default:
+ break;
+ }
+out:
+ mutex_unlock(&smp_cpu_state_mutex);
+ put_online_cpus();
+ return rc ? rc : count;
+}
+static DEVICE_ATTR(configure, 0644, cpu_configure_show, cpu_configure_store);
+#endif /* CONFIG_HOTPLUG_CPU */
+
+static ssize_t show_cpu_address(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", pcpu_devices[dev->id].address);
+}
+static DEVICE_ATTR(address, 0444, show_cpu_address, NULL);
+
+static struct attribute *cpu_common_attrs[] = {
+#ifdef CONFIG_HOTPLUG_CPU
+ &dev_attr_configure.attr,
+#endif
+ &dev_attr_address.attr,
+ NULL,
+};
+
+static struct attribute_group cpu_common_attr_group = {
+ .attrs = cpu_common_attrs,
+};
+
+static ssize_t show_capability(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int capability;
+ int rc;
+
+ rc = get_cpu_capability(&capability);
+ if (rc)
+ return rc;
+ return sprintf(buf, "%u\n", capability);
+}
+static DEVICE_ATTR(capability, 0444, show_capability, NULL);
+
+static ssize_t show_idle_count(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id);
+ unsigned long long idle_count;
+ unsigned int sequence;
+
+ do {
+ sequence = ACCESS_ONCE(idle->sequence);
+ idle_count = ACCESS_ONCE(idle->idle_count);
+ if (ACCESS_ONCE(idle->idle_enter))
+ idle_count++;
+ } while ((sequence & 1) || (idle->sequence != sequence));
+ return sprintf(buf, "%llu\n", idle_count);
+}
+static DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL);
+
+static ssize_t show_idle_time(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id);
+ unsigned long long now, idle_time, idle_enter, idle_exit;
+ unsigned int sequence;
+
+ do {
+ now = get_clock();
+ sequence = ACCESS_ONCE(idle->sequence);
+ idle_time = ACCESS_ONCE(idle->idle_time);
+ idle_enter = ACCESS_ONCE(idle->idle_enter);
+ idle_exit = ACCESS_ONCE(idle->idle_exit);
+ } while ((sequence & 1) || (idle->sequence != sequence));
+ idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0;
+ return sprintf(buf, "%llu\n", idle_time >> 12);
+}
+static DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);
+
+static struct attribute *cpu_online_attrs[] = {
+ &dev_attr_capability.attr,
+ &dev_attr_idle_count.attr,
+ &dev_attr_idle_time_us.attr,
+ NULL,
+};
+
+static struct attribute_group cpu_online_attr_group = {
+ .attrs = cpu_online_attrs,
+};
+
+static int __cpuinit smp_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ unsigned int cpu = (unsigned int)(long)hcpu;
+ struct cpu *c = &pcpu_devices[cpu].cpu;
+ struct device *s = &c->dev;
+ struct s390_idle_data *idle;
+ int err = 0;
+
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ idle = &per_cpu(s390_idle, cpu);
+ memset(idle, 0, sizeof(struct s390_idle_data));
+ err = sysfs_create_group(&s->kobj, &cpu_online_attr_group);
+ break;
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ sysfs_remove_group(&s->kobj, &cpu_online_attr_group);
+ break;
+ }
+ return notifier_from_errno(err);
+}
+
+static struct notifier_block __cpuinitdata smp_cpu_nb = {
+ .notifier_call = smp_cpu_notify,
+};
+
+static int __devinit smp_add_present_cpu(int cpu)
+{
+ struct cpu *c = &pcpu_devices[cpu].cpu;
+ struct device *s = &c->dev;
+ int rc;
+
+ c->hotpluggable = 1;
+ rc = register_cpu(c, cpu);
+ if (rc)
+ goto out;
+ rc = sysfs_create_group(&s->kobj, &cpu_common_attr_group);
+ if (rc)
+ goto out_cpu;
+ if (cpu_online(cpu)) {
+ rc = sysfs_create_group(&s->kobj, &cpu_online_attr_group);
+ if (rc)
+ goto out_online;
+ }
+ rc = topology_cpu_init(c);
+ if (rc)
+ goto out_topology;
+ return 0;
+
+out_topology:
+ if (cpu_online(cpu))
+ sysfs_remove_group(&s->kobj, &cpu_online_attr_group);
+out_online:
+ sysfs_remove_group(&s->kobj, &cpu_common_attr_group);
+out_cpu:
+#ifdef CONFIG_HOTPLUG_CPU
+ unregister_cpu(c);
+#endif
+out:
+ return rc;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+int __ref smp_rescan_cpus(void)
+{
+ struct sclp_cpu_info *info;
+ int nr;
+
+ info = smp_get_cpu_info();
+ if (!info)
+ return -ENOMEM;
+ get_online_cpus();
+ mutex_lock(&smp_cpu_state_mutex);
+ nr = __smp_rescan_cpus(info, 1);
+ mutex_unlock(&smp_cpu_state_mutex);
+ put_online_cpus();
+ kfree(info);
+ if (nr)
+ topology_schedule_update();
+ return 0;
+}
+
+static ssize_t __ref rescan_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int rc;
+
+ rc = smp_rescan_cpus();
+ return rc ? rc : count;
+}
+static DEVICE_ATTR(rescan, 0200, NULL, rescan_store);
+#endif /* CONFIG_HOTPLUG_CPU */
+
+static int __init s390_smp_init(void)
+{
+ int cpu, rc;
+
+ register_cpu_notifier(&smp_cpu_nb);
+#ifdef CONFIG_HOTPLUG_CPU
+ rc = device_create_file(cpu_subsys.dev_root, &dev_attr_rescan);
+ if (rc)
+ return rc;
+#endif
+ for_each_present_cpu(cpu) {
+ rc = smp_add_present_cpu(cpu);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+subsys_initcall(s390_smp_init);
diff --git a/arch/s390/kernel/stacktrace.c b/arch/s390/kernel/stacktrace.c
new file mode 100644
index 00000000..8841919e
--- /dev/null
+++ b/arch/s390/kernel/stacktrace.c
@@ -0,0 +1,98 @@
+/*
+ * arch/s390/kernel/stacktrace.c
+ *
+ * Stack trace management functions
+ *
+ * Copyright (C) IBM Corp. 2006
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+#include <linux/kallsyms.h>
+#include <linux/module.h>
+
+static unsigned long save_context_stack(struct stack_trace *trace,
+ unsigned long sp,
+ unsigned long low,
+ unsigned long high,
+ int savesched)
+{
+ struct stack_frame *sf;
+ struct pt_regs *regs;
+ unsigned long addr;
+
+ while(1) {
+ sp &= PSW_ADDR_INSN;
+ if (sp < low || sp > high)
+ return sp;
+ sf = (struct stack_frame *)sp;
+ while(1) {
+ addr = sf->gprs[8] & PSW_ADDR_INSN;
+ if (!trace->skip)
+ trace->entries[trace->nr_entries++] = addr;
+ else
+ trace->skip--;
+ if (trace->nr_entries >= trace->max_entries)
+ return sp;
+ low = sp;
+ sp = sf->back_chain & PSW_ADDR_INSN;
+ if (!sp)
+ break;
+ if (sp <= low || sp > high - sizeof(*sf))
+ return sp;
+ sf = (struct stack_frame *)sp;
+ }
+ /* Zero backchain detected, check for interrupt frame. */
+ sp = (unsigned long)(sf + 1);
+ if (sp <= low || sp > high - sizeof(*regs))
+ return sp;
+ regs = (struct pt_regs *)sp;
+ addr = regs->psw.addr & PSW_ADDR_INSN;
+ if (savesched || !in_sched_functions(addr)) {
+ if (!trace->skip)
+ trace->entries[trace->nr_entries++] = addr;
+ else
+ trace->skip--;
+ }
+ if (trace->nr_entries >= trace->max_entries)
+ return sp;
+ low = sp;
+ sp = regs->gprs[15];
+ }
+}
+
+void save_stack_trace(struct stack_trace *trace)
+{
+ register unsigned long sp asm ("15");
+ unsigned long orig_sp, new_sp;
+
+ orig_sp = sp & PSW_ADDR_INSN;
+ new_sp = save_context_stack(trace, orig_sp,
+ S390_lowcore.panic_stack - PAGE_SIZE,
+ S390_lowcore.panic_stack, 1);
+ if (new_sp != orig_sp)
+ return;
+ new_sp = save_context_stack(trace, new_sp,
+ S390_lowcore.async_stack - ASYNC_SIZE,
+ S390_lowcore.async_stack, 1);
+ if (new_sp != orig_sp)
+ return;
+ save_context_stack(trace, new_sp,
+ S390_lowcore.thread_info,
+ S390_lowcore.thread_info + THREAD_SIZE, 1);
+}
+EXPORT_SYMBOL_GPL(save_stack_trace);
+
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+ unsigned long sp, low, high;
+
+ sp = tsk->thread.ksp & PSW_ADDR_INSN;
+ low = (unsigned long) task_stack_page(tsk);
+ high = (unsigned long) task_pt_regs(tsk);
+ save_context_stack(trace, sp, low, high, 0);
+ if (trace->nr_entries < trace->max_entries)
+ trace->entries[trace->nr_entries++] = ULONG_MAX;
+}
+EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
diff --git a/arch/s390/kernel/suspend.c b/arch/s390/kernel/suspend.c
new file mode 100644
index 00000000..aa1494d0
--- /dev/null
+++ b/arch/s390/kernel/suspend.c
@@ -0,0 +1,182 @@
+/*
+ * Suspend support specific for s390.
+ *
+ * Copyright IBM Corp. 2009
+ *
+ * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
+ */
+
+#include <linux/pfn.h>
+#include <linux/suspend.h>
+#include <linux/mm.h>
+#include <asm/ctl_reg.h>
+
+/*
+ * References to section boundaries
+ */
+extern const void __nosave_begin, __nosave_end;
+
+/*
+ * The restore of the saved pages in an hibernation image will set
+ * the change and referenced bits in the storage key for each page.
+ * Overindication of the referenced bits after an hibernation cycle
+ * does not cause any harm but the overindication of the change bits
+ * would cause trouble.
+ * Use the ARCH_SAVE_PAGE_KEYS hooks to save the storage key of each
+ * page to the most significant byte of the associated page frame
+ * number in the hibernation image.
+ */
+
+/*
+ * Key storage is allocated as a linked list of pages.
+ * The size of the keys array is (PAGE_SIZE - sizeof(long))
+ */
+struct page_key_data {
+ struct page_key_data *next;
+ unsigned char data[];
+};
+
+#define PAGE_KEY_DATA_SIZE (PAGE_SIZE - sizeof(struct page_key_data *))
+
+static struct page_key_data *page_key_data;
+static struct page_key_data *page_key_rp, *page_key_wp;
+static unsigned long page_key_rx, page_key_wx;
+
+/*
+ * For each page in the hibernation image one additional byte is
+ * stored in the most significant byte of the page frame number.
+ * On suspend no additional memory is required but on resume the
+ * keys need to be memorized until the page data has been restored.
+ * Only then can the storage keys be set to their old state.
+ */
+unsigned long page_key_additional_pages(unsigned long pages)
+{
+ return DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE);
+}
+
+/*
+ * Free page_key_data list of arrays.
+ */
+void page_key_free(void)
+{
+ struct page_key_data *pkd;
+
+ while (page_key_data) {
+ pkd = page_key_data;
+ page_key_data = pkd->next;
+ free_page((unsigned long) pkd);
+ }
+}
+
+/*
+ * Allocate page_key_data list of arrays with enough room to store
+ * one byte for each page in the hibernation image.
+ */
+int page_key_alloc(unsigned long pages)
+{
+ struct page_key_data *pk;
+ unsigned long size;
+
+ size = DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE);
+ while (size--) {
+ pk = (struct page_key_data *) get_zeroed_page(GFP_KERNEL);
+ if (!pk) {
+ page_key_free();
+ return -ENOMEM;
+ }
+ pk->next = page_key_data;
+ page_key_data = pk;
+ }
+ page_key_rp = page_key_wp = page_key_data;
+ page_key_rx = page_key_wx = 0;
+ return 0;
+}
+
+/*
+ * Save the storage key into the upper 8 bits of the page frame number.
+ */
+void page_key_read(unsigned long *pfn)
+{
+ unsigned long addr;
+
+ addr = (unsigned long) page_address(pfn_to_page(*pfn));
+ *(unsigned char *) pfn = (unsigned char) page_get_storage_key(addr);
+}
+
+/*
+ * Extract the storage key from the upper 8 bits of the page frame number
+ * and store it in the page_key_data list of arrays.
+ */
+void page_key_memorize(unsigned long *pfn)
+{
+ page_key_wp->data[page_key_wx] = *(unsigned char *) pfn;
+ *(unsigned char *) pfn = 0;
+ if (++page_key_wx < PAGE_KEY_DATA_SIZE)
+ return;
+ page_key_wp = page_key_wp->next;
+ page_key_wx = 0;
+}
+
+/*
+ * Get the next key from the page_key_data list of arrays and set the
+ * storage key of the page referred by @address. If @address refers to
+ * a "safe" page the swsusp_arch_resume code will transfer the storage
+ * key from the buffer page to the original page.
+ */
+void page_key_write(void *address)
+{
+ page_set_storage_key((unsigned long) address,
+ page_key_rp->data[page_key_rx], 0);
+ if (++page_key_rx >= PAGE_KEY_DATA_SIZE)
+ return;
+ page_key_rp = page_key_rp->next;
+ page_key_rx = 0;
+}
+
+int pfn_is_nosave(unsigned long pfn)
+{
+ unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin));
+ unsigned long nosave_end_pfn = PFN_DOWN(__pa(&__nosave_end));
+
+ /* Always save lowcore pages (LC protection might be enabled). */
+ if (pfn <= LC_PAGES)
+ return 0;
+ if (pfn >= nosave_begin_pfn && pfn < nosave_end_pfn)
+ return 1;
+ /* Skip memory holes and read-only pages (NSS, DCSS, ...). */
+ if (tprot(PFN_PHYS(pfn)))
+ return 1;
+ return 0;
+}
+
+void save_processor_state(void)
+{
+ /* swsusp_arch_suspend() actually saves all cpu register contents.
+ * Machine checks must be disabled since swsusp_arch_suspend() stores
+ * register contents to their lowcore save areas. That's the same
+ * place where register contents on machine checks would be saved.
+ * To avoid register corruption disable machine checks.
+ * We must also disable machine checks in the new psw mask for
+ * program checks, since swsusp_arch_suspend() may generate program
+ * checks. Disabling machine checks for all other new psw masks is
+ * just paranoia.
+ */
+ local_mcck_disable();
+ /* Disable lowcore protection */
+ __ctl_clear_bit(0,28);
+ S390_lowcore.external_new_psw.mask &= ~PSW_MASK_MCHECK;
+ S390_lowcore.svc_new_psw.mask &= ~PSW_MASK_MCHECK;
+ S390_lowcore.io_new_psw.mask &= ~PSW_MASK_MCHECK;
+ S390_lowcore.program_new_psw.mask &= ~PSW_MASK_MCHECK;
+}
+
+void restore_processor_state(void)
+{
+ S390_lowcore.external_new_psw.mask |= PSW_MASK_MCHECK;
+ S390_lowcore.svc_new_psw.mask |= PSW_MASK_MCHECK;
+ S390_lowcore.io_new_psw.mask |= PSW_MASK_MCHECK;
+ S390_lowcore.program_new_psw.mask |= PSW_MASK_MCHECK;
+ /* Enable lowcore protection */
+ __ctl_set_bit(0,28);
+ local_mcck_enable();
+}
diff --git a/arch/s390/kernel/swsusp_asm64.S b/arch/s390/kernel/swsusp_asm64.S
new file mode 100644
index 00000000..dd70ef04
--- /dev/null
+++ b/arch/s390/kernel/swsusp_asm64.S
@@ -0,0 +1,285 @@
+/*
+ * S390 64-bit swsusp implementation
+ *
+ * Copyright IBM Corp. 2009
+ *
+ * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
+ * Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#include <linux/linkage.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/thread_info.h>
+#include <asm/asm-offsets.h>
+
+/*
+ * Save register context in absolute 0 lowcore and call swsusp_save() to
+ * create in-memory kernel image. The context is saved in the designated
+ * "store status" memory locations (see POP).
+ * We return from this function twice. The first time during the suspend to
+ * disk process. The second time via the swsusp_arch_resume() function
+ * (see below) in the resume process.
+ * This function runs with disabled interrupts.
+ */
+ .section .text
+ENTRY(swsusp_arch_suspend)
+ stmg %r6,%r15,__SF_GPRS(%r15)
+ lgr %r1,%r15
+ aghi %r15,-STACK_FRAME_OVERHEAD
+ stg %r1,__SF_BACKCHAIN(%r15)
+
+ /* Deactivate DAT */
+ stnsm __SF_EMPTY(%r15),0xfb
+
+ /* Store prefix register on stack */
+ stpx __SF_EMPTY(%r15)
+
+ /* Save prefix register contents for lowcore */
+ llgf %r4,__SF_EMPTY(%r15)
+
+ /* Get pointer to save area */
+ lghi %r1,0x1000
+
+ /* Save CPU address */
+ stap __LC_EXT_CPU_ADDR(%r0)
+
+ /* Store registers */
+ mvc 0x318(4,%r1),__SF_EMPTY(%r15) /* move prefix to lowcore */
+ stfpc 0x31c(%r1) /* store fpu control */
+ std 0,0x200(%r1) /* store f0 */
+ std 1,0x208(%r1) /* store f1 */
+ std 2,0x210(%r1) /* store f2 */
+ std 3,0x218(%r1) /* store f3 */
+ std 4,0x220(%r1) /* store f4 */
+ std 5,0x228(%r1) /* store f5 */
+ std 6,0x230(%r1) /* store f6 */
+ std 7,0x238(%r1) /* store f7 */
+ std 8,0x240(%r1) /* store f8 */
+ std 9,0x248(%r1) /* store f9 */
+ std 10,0x250(%r1) /* store f10 */
+ std 11,0x258(%r1) /* store f11 */
+ std 12,0x260(%r1) /* store f12 */
+ std 13,0x268(%r1) /* store f13 */
+ std 14,0x270(%r1) /* store f14 */
+ std 15,0x278(%r1) /* store f15 */
+ stam %a0,%a15,0x340(%r1) /* store access registers */
+ stctg %c0,%c15,0x380(%r1) /* store control registers */
+ stmg %r0,%r15,0x280(%r1) /* store general registers */
+
+ stpt 0x328(%r1) /* store timer */
+ stck __SF_EMPTY(%r15) /* store clock */
+ stckc 0x330(%r1) /* store clock comparator */
+
+ /* Update cputime accounting before going to sleep */
+ lg %r0,__LC_LAST_UPDATE_TIMER
+ slg %r0,0x328(%r1)
+ alg %r0,__LC_SYSTEM_TIMER
+ stg %r0,__LC_SYSTEM_TIMER
+ mvc __LC_LAST_UPDATE_TIMER(8),0x328(%r1)
+ lg %r0,__LC_LAST_UPDATE_CLOCK
+ slg %r0,__SF_EMPTY(%r15)
+ alg %r0,__LC_STEAL_TIMER
+ stg %r0,__LC_STEAL_TIMER
+ mvc __LC_LAST_UPDATE_CLOCK(8),__SF_EMPTY(%r15)
+
+ /* Activate DAT */
+ stosm __SF_EMPTY(%r15),0x04
+
+ /* Set prefix page to zero */
+ xc __SF_EMPTY(4,%r15),__SF_EMPTY(%r15)
+ spx __SF_EMPTY(%r15)
+
+ lghi %r2,0
+ lghi %r3,2*PAGE_SIZE
+ lghi %r5,2*PAGE_SIZE
+1: mvcle %r2,%r4,0
+ jo 1b
+
+ /* Save image */
+ brasl %r14,swsusp_save
+
+ /* Restore prefix register and return */
+ lghi %r1,0x1000
+ spx 0x318(%r1)
+ lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
+ lghi %r2,0
+ br %r14
+
+/*
+ * Restore saved memory image to correct place and restore register context.
+ * Then we return to the function that called swsusp_arch_suspend().
+ * swsusp_arch_resume() runs with disabled interrupts.
+ */
+ENTRY(swsusp_arch_resume)
+ stmg %r6,%r15,__SF_GPRS(%r15)
+ lgr %r1,%r15
+ aghi %r15,-STACK_FRAME_OVERHEAD
+ stg %r1,__SF_BACKCHAIN(%r15)
+
+ /* Make all free pages stable */
+ lghi %r2,1
+ brasl %r14,arch_set_page_states
+
+ /* Deactivate DAT */
+ stnsm __SF_EMPTY(%r15),0xfb
+
+ /* Set prefix page to zero */
+ xc __SF_EMPTY(4,%r15),__SF_EMPTY(%r15)
+ spx __SF_EMPTY(%r15)
+
+ /* Restore saved image */
+ larl %r1,restore_pblist
+ lg %r1,0(%r1)
+ ltgr %r1,%r1
+ jz 2f
+0:
+ lg %r2,8(%r1)
+ lg %r4,0(%r1)
+ iske %r0,%r4
+ lghi %r3,PAGE_SIZE
+ lghi %r5,PAGE_SIZE
+1:
+ mvcle %r2,%r4,0
+ jo 1b
+ lg %r2,8(%r1)
+ sske %r0,%r2
+ lg %r1,16(%r1)
+ ltgr %r1,%r1
+ jnz 0b
+2:
+ ptlb /* flush tlb */
+
+ /* Reset System */
+ larl %r1,restart_entry
+ larl %r2,.Lrestart_diag308_psw
+ og %r1,0(%r2)
+ stg %r1,0(%r0)
+ larl %r1,.Lnew_pgm_check_psw
+ epsw %r2,%r3
+ stm %r2,%r3,0(%r1)
+ mvc __LC_PGM_NEW_PSW(16,%r0),0(%r1)
+ lghi %r0,0
+ diag %r0,%r0,0x308
+restart_entry:
+ lhi %r1,1
+ sigp %r1,%r0,0x12
+ sam64
+ larl %r1,.Lnew_pgm_check_psw
+ lpswe 0(%r1)
+pgm_check_entry:
+
+ /* Switch to original suspend CPU */
+ larl %r1,.Lresume_cpu /* Resume CPU address: r2 */
+ stap 0(%r1)
+ llgh %r2,0(%r1)
+ llgh %r1,__LC_EXT_CPU_ADDR(%r0) /* Suspend CPU address: r1 */
+ cgr %r1,%r2
+ je restore_registers /* r1 = r2 -> nothing to do */
+ larl %r4,.Lrestart_suspend_psw /* Set new restart PSW */
+ mvc __LC_RST_NEW_PSW(16,%r0),0(%r4)
+3:
+ sigp %r9,%r1,11 /* sigp initial cpu reset */
+ brc 8,4f /* accepted */
+ brc 2,3b /* busy, try again */
+
+ /* Suspend CPU not available -> panic */
+ larl %r15,init_thread_union
+ ahi %r15,1<<(PAGE_SHIFT+THREAD_ORDER)
+ larl %r2,.Lpanic_string
+ larl %r3,_sclp_print_early
+ lghi %r1,0
+ sam31
+ sigp %r1,%r0,0x12
+ basr %r14,%r3
+ larl %r3,.Ldisabled_wait_31
+ lpsw 0(%r3)
+4:
+ /* Switch to suspend CPU */
+ sigp %r9,%r1,6 /* sigp restart to suspend CPU */
+ brc 2,4b /* busy, try again */
+5:
+ sigp %r9,%r2,5 /* sigp stop to current resume CPU */
+ brc 2,5b /* busy, try again */
+6: j 6b
+
+restart_suspend:
+ larl %r1,.Lresume_cpu
+ llgh %r2,0(%r1)
+7:
+ sigp %r9,%r2,1 /* sigp sense, wait for resume CPU */
+ brc 8,7b /* accepted, status 0, still running */
+ brc 2,7b /* busy, try again */
+ tmll %r9,0x40 /* Test if resume CPU is stopped */
+ jz 7b
+
+restore_registers:
+ /* Restore registers */
+ lghi %r13,0x1000 /* %r1 = pointer to save area */
+
+ /* Ignore time spent in suspended state. */
+ llgf %r1,0x318(%r13)
+ stck __LC_LAST_UPDATE_CLOCK(%r1)
+ spt 0x328(%r13) /* reprogram timer */
+ //sckc 0x330(%r13) /* set clock comparator */
+
+ lctlg %c0,%c15,0x380(%r13) /* load control registers */
+ lam %a0,%a15,0x340(%r13) /* load access registers */
+
+ lfpc 0x31c(%r13) /* load fpu control */
+ ld 0,0x200(%r13) /* load f0 */
+ ld 1,0x208(%r13) /* load f1 */
+ ld 2,0x210(%r13) /* load f2 */
+ ld 3,0x218(%r13) /* load f3 */
+ ld 4,0x220(%r13) /* load f4 */
+ ld 5,0x228(%r13) /* load f5 */
+ ld 6,0x230(%r13) /* load f6 */
+ ld 7,0x238(%r13) /* load f7 */
+ ld 8,0x240(%r13) /* load f8 */
+ ld 9,0x248(%r13) /* load f9 */
+ ld 10,0x250(%r13) /* load f10 */
+ ld 11,0x258(%r13) /* load f11 */
+ ld 12,0x260(%r13) /* load f12 */
+ ld 13,0x268(%r13) /* load f13 */
+ ld 14,0x270(%r13) /* load f14 */
+ ld 15,0x278(%r13) /* load f15 */
+
+ /* Load old stack */
+ lg %r15,0x2f8(%r13)
+
+ /* Restore prefix register */
+ spx 0x318(%r13)
+
+ /* Activate DAT */
+ stosm __SF_EMPTY(%r15),0x04
+
+ /* Make all free pages unstable */
+ lghi %r2,0
+ brasl %r14,arch_set_page_states
+
+ /* Log potential guest relocation */
+ brasl %r14,lgr_info_log
+
+ /* Reinitialize the channel subsystem */
+ brasl %r14,channel_subsystem_reinit
+
+ /* Return 0 */
+ lmg %r6,%r15,STACK_FRAME_OVERHEAD + __SF_GPRS(%r15)
+ lghi %r2,0
+ br %r14
+
+ .section .data..nosave,"aw",@progbits
+ .align 8
+.Ldisabled_wait_31:
+ .long 0x000a0000,0x00000000
+.Lpanic_string:
+ .asciz "Resume not possible because suspend CPU is no longer available"
+ .align 8
+.Lrestart_diag308_psw:
+ .long 0x00080000,0x80000000
+.Lrestart_suspend_psw:
+ .quad 0x0000000180000000,restart_suspend
+.Lnew_pgm_check_psw:
+ .quad 0,pgm_check_entry
+.Lresume_cpu:
+ .byte 0,0
diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c
new file mode 100644
index 00000000..78ea1948
--- /dev/null
+++ b/arch/s390/kernel/sys_s390.c
@@ -0,0 +1,151 @@
+/*
+ * arch/s390/kernel/sys_s390.c
+ *
+ * S390 version
+ * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ * Thomas Spatzier (tspat@de.ibm.com)
+ *
+ * Derived from "arch/i386/kernel/sys_i386.c"
+ *
+ * This file contains various random system calls that
+ * have a non-standard calling sequence on the Linux/s390
+ * platform.
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/smp.h>
+#include <linux/sem.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <linux/stat.h>
+#include <linux/syscalls.h>
+#include <linux/mman.h>
+#include <linux/file.h>
+#include <linux/utsname.h>
+#include <linux/personality.h>
+#include <linux/unistd.h>
+#include <linux/ipc.h>
+#include <asm/uaccess.h>
+#include "entry.h"
+
+/*
+ * Perform the mmap() system call. Linux for S/390 isn't able to handle more
+ * than 5 system call parameters, so this system call uses a memory block
+ * for parameter passing.
+ */
+
+struct s390_mmap_arg_struct {
+ unsigned long addr;
+ unsigned long len;
+ unsigned long prot;
+ unsigned long flags;
+ unsigned long fd;
+ unsigned long offset;
+};
+
+SYSCALL_DEFINE1(mmap2, struct s390_mmap_arg_struct __user *, arg)
+{
+ struct s390_mmap_arg_struct a;
+ int error = -EFAULT;
+
+ if (copy_from_user(&a, arg, sizeof(a)))
+ goto out;
+ error = sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
+out:
+ return error;
+}
+
+/*
+ * sys_ipc() is the de-multiplexer for the SysV IPC calls.
+ */
+SYSCALL_DEFINE5(s390_ipc, uint, call, int, first, unsigned long, second,
+ unsigned long, third, void __user *, ptr)
+{
+ if (call >> 16)
+ return -EINVAL;
+ /* The s390 sys_ipc variant has only five parameters instead of six
+ * like the generic variant. The only difference is the handling of
+ * the SEMTIMEDOP subcall where on s390 the third parameter is used
+ * as a pointer to a struct timespec where the generic variant uses
+ * the fifth parameter.
+ * Therefore we can call the generic variant by simply passing the
+ * third parameter also as fifth parameter.
+ */
+ return sys_ipc(call, first, second, third, ptr, third);
+}
+
+#ifdef CONFIG_64BIT
+SYSCALL_DEFINE1(s390_personality, unsigned int, personality)
+{
+ unsigned int ret;
+
+ if (current->personality == PER_LINUX32 && personality == PER_LINUX)
+ personality = PER_LINUX32;
+ ret = sys_personality(personality);
+ if (ret == PER_LINUX32)
+ ret = PER_LINUX;
+
+ return ret;
+}
+#endif /* CONFIG_64BIT */
+
+/*
+ * Wrapper function for sys_fadvise64/fadvise64_64
+ */
+#ifndef CONFIG_64BIT
+
+SYSCALL_DEFINE5(s390_fadvise64, int, fd, u32, offset_high, u32, offset_low,
+ size_t, len, int, advice)
+{
+ return sys_fadvise64(fd, (u64) offset_high << 32 | offset_low,
+ len, advice);
+}
+
+struct fadvise64_64_args {
+ int fd;
+ long long offset;
+ long long len;
+ int advice;
+};
+
+SYSCALL_DEFINE1(s390_fadvise64_64, struct fadvise64_64_args __user *, args)
+{
+ struct fadvise64_64_args a;
+
+ if ( copy_from_user(&a, args, sizeof(a)) )
+ return -EFAULT;
+ return sys_fadvise64_64(a.fd, a.offset, a.len, a.advice);
+}
+
+/*
+ * This is a wrapper to call sys_fallocate(). For 31 bit s390 the last
+ * 64 bit argument "len" is split into the upper and lower 32 bits. The
+ * system call wrapper in the user space loads the value to %r6/%r7.
+ * The code in entry.S keeps the values in %r2 - %r6 where they are and
+ * stores %r7 to 96(%r15). But the standard C linkage requires that
+ * the whole 64 bit value for len is stored on the stack and doesn't
+ * use %r6 at all. So s390_fallocate has to convert the arguments from
+ * %r2: fd, %r3: mode, %r4/%r5: offset, %r6/96(%r15)-99(%r15): len
+ * to
+ * %r2: fd, %r3: mode, %r4/%r5: offset, 96(%r15)-103(%r15): len
+ */
+SYSCALL_DEFINE(s390_fallocate)(int fd, int mode, loff_t offset,
+ u32 len_high, u32 len_low)
+{
+ return sys_fallocate(fd, mode, offset, ((u64)len_high << 32) | len_low);
+}
+#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS
+asmlinkage long SyS_s390_fallocate(long fd, long mode, loff_t offset,
+ long len_high, long len_low)
+{
+ return SYSC_s390_fallocate((int) fd, (int) mode, offset,
+ (u32) len_high, (u32) len_low);
+}
+SYSCALL_ALIAS(sys_s390_fallocate, SyS_s390_fallocate);
+#endif
+
+#endif
diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S
new file mode 100644
index 00000000..bcab2f04
--- /dev/null
+++ b/arch/s390/kernel/syscalls.S
@@ -0,0 +1,352 @@
+/*
+ * definitions for sys_call_table, each line represents an
+ * entry in the table in the form
+ * SYSCALL(31 bit syscall, 64 bit syscall, 31 bit emulated syscall)
+ *
+ * this file is meant to be included from entry.S and entry64.S
+ */
+
+#define NI_SYSCALL SYSCALL(sys_ni_syscall,sys_ni_syscall,sys_ni_syscall)
+
+NI_SYSCALL /* 0 */
+SYSCALL(sys_exit,sys_exit,sys32_exit_wrapper)
+SYSCALL(sys_fork,sys_fork,sys_fork)
+SYSCALL(sys_read,sys_read,sys32_read_wrapper)
+SYSCALL(sys_write,sys_write,sys32_write_wrapper)
+SYSCALL(sys_open,sys_open,sys32_open_wrapper) /* 5 */
+SYSCALL(sys_close,sys_close,sys32_close_wrapper)
+SYSCALL(sys_restart_syscall,sys_restart_syscall,sys_restart_syscall)
+SYSCALL(sys_creat,sys_creat,sys32_creat_wrapper)
+SYSCALL(sys_link,sys_link,sys32_link_wrapper)
+SYSCALL(sys_unlink,sys_unlink,sys32_unlink_wrapper) /* 10 */
+SYSCALL(sys_execve,sys_execve,sys32_execve_wrapper)
+SYSCALL(sys_chdir,sys_chdir,sys32_chdir_wrapper)
+SYSCALL(sys_time,sys_ni_syscall,sys32_time_wrapper) /* old time syscall */
+SYSCALL(sys_mknod,sys_mknod,sys32_mknod_wrapper)
+SYSCALL(sys_chmod,sys_chmod,sys32_chmod_wrapper) /* 15 */
+SYSCALL(sys_lchown16,sys_ni_syscall,sys32_lchown16_wrapper) /* old lchown16 syscall*/
+NI_SYSCALL /* old break syscall holder */
+NI_SYSCALL /* old stat syscall holder */
+SYSCALL(sys_lseek,sys_lseek,sys32_lseek_wrapper)
+SYSCALL(sys_getpid,sys_getpid,sys_getpid) /* 20 */
+SYSCALL(sys_mount,sys_mount,sys32_mount_wrapper)
+SYSCALL(sys_oldumount,sys_oldumount,sys32_oldumount_wrapper)
+SYSCALL(sys_setuid16,sys_ni_syscall,sys32_setuid16_wrapper) /* old setuid16 syscall*/
+SYSCALL(sys_getuid16,sys_ni_syscall,sys32_getuid16) /* old getuid16 syscall*/
+SYSCALL(sys_stime,sys_ni_syscall,sys32_stime_wrapper) /* 25 old stime syscall */
+SYSCALL(sys_ptrace,sys_ptrace,sys32_ptrace_wrapper)
+SYSCALL(sys_alarm,sys_alarm,sys32_alarm_wrapper)
+NI_SYSCALL /* old fstat syscall */
+SYSCALL(sys_pause,sys_pause,sys_pause)
+SYSCALL(sys_utime,sys_utime,compat_sys_utime_wrapper) /* 30 */
+NI_SYSCALL /* old stty syscall */
+NI_SYSCALL /* old gtty syscall */
+SYSCALL(sys_access,sys_access,sys32_access_wrapper)
+SYSCALL(sys_nice,sys_nice,sys32_nice_wrapper)
+NI_SYSCALL /* 35 old ftime syscall */
+SYSCALL(sys_sync,sys_sync,sys_sync)
+SYSCALL(sys_kill,sys_kill,sys32_kill_wrapper)
+SYSCALL(sys_rename,sys_rename,sys32_rename_wrapper)
+SYSCALL(sys_mkdir,sys_mkdir,sys32_mkdir_wrapper)
+SYSCALL(sys_rmdir,sys_rmdir,sys32_rmdir_wrapper) /* 40 */
+SYSCALL(sys_dup,sys_dup,sys32_dup_wrapper)
+SYSCALL(sys_pipe,sys_pipe,sys32_pipe_wrapper)
+SYSCALL(sys_times,sys_times,compat_sys_times_wrapper)
+NI_SYSCALL /* old prof syscall */
+SYSCALL(sys_brk,sys_brk,sys32_brk_wrapper) /* 45 */
+SYSCALL(sys_setgid16,sys_ni_syscall,sys32_setgid16_wrapper) /* old setgid16 syscall*/
+SYSCALL(sys_getgid16,sys_ni_syscall,sys32_getgid16) /* old getgid16 syscall*/
+SYSCALL(sys_signal,sys_signal,sys32_signal_wrapper)
+SYSCALL(sys_geteuid16,sys_ni_syscall,sys32_geteuid16) /* old geteuid16 syscall */
+SYSCALL(sys_getegid16,sys_ni_syscall,sys32_getegid16) /* 50 old getegid16 syscall */
+SYSCALL(sys_acct,sys_acct,sys32_acct_wrapper)
+SYSCALL(sys_umount,sys_umount,sys32_umount_wrapper)
+NI_SYSCALL /* old lock syscall */
+SYSCALL(sys_ioctl,sys_ioctl,compat_sys_ioctl_wrapper)
+SYSCALL(sys_fcntl,sys_fcntl,compat_sys_fcntl_wrapper) /* 55 */
+NI_SYSCALL /* intel mpx syscall */
+SYSCALL(sys_setpgid,sys_setpgid,sys32_setpgid_wrapper)
+NI_SYSCALL /* old ulimit syscall */
+NI_SYSCALL /* old uname syscall */
+SYSCALL(sys_umask,sys_umask,sys32_umask_wrapper) /* 60 */
+SYSCALL(sys_chroot,sys_chroot,sys32_chroot_wrapper)
+SYSCALL(sys_ustat,sys_ustat,sys32_ustat_wrapper)
+SYSCALL(sys_dup2,sys_dup2,sys32_dup2_wrapper)
+SYSCALL(sys_getppid,sys_getppid,sys_getppid)
+SYSCALL(sys_getpgrp,sys_getpgrp,sys_getpgrp) /* 65 */
+SYSCALL(sys_setsid,sys_setsid,sys_setsid)
+SYSCALL(sys_sigaction,sys_sigaction,sys32_sigaction_wrapper)
+NI_SYSCALL /* old sgetmask syscall*/
+NI_SYSCALL /* old ssetmask syscall*/
+SYSCALL(sys_setreuid16,sys_ni_syscall,sys32_setreuid16_wrapper) /* old setreuid16 syscall */
+SYSCALL(sys_setregid16,sys_ni_syscall,sys32_setregid16_wrapper) /* old setregid16 syscall */
+SYSCALL(sys_sigsuspend,sys_sigsuspend,sys_sigsuspend_wrapper)
+SYSCALL(sys_sigpending,sys_sigpending,compat_sys_sigpending_wrapper)
+SYSCALL(sys_sethostname,sys_sethostname,sys32_sethostname_wrapper)
+SYSCALL(sys_setrlimit,sys_setrlimit,compat_sys_setrlimit_wrapper) /* 75 */
+SYSCALL(sys_old_getrlimit,sys_getrlimit,compat_sys_old_getrlimit_wrapper)
+SYSCALL(sys_getrusage,sys_getrusage,compat_sys_getrusage_wrapper)
+SYSCALL(sys_gettimeofday,sys_gettimeofday,compat_sys_gettimeofday_wrapper)
+SYSCALL(sys_settimeofday,sys_settimeofday,compat_sys_settimeofday_wrapper)
+SYSCALL(sys_getgroups16,sys_ni_syscall,sys32_getgroups16_wrapper) /* 80 old getgroups16 syscall */
+SYSCALL(sys_setgroups16,sys_ni_syscall,sys32_setgroups16_wrapper) /* old setgroups16 syscall */
+NI_SYSCALL /* old select syscall */
+SYSCALL(sys_symlink,sys_symlink,sys32_symlink_wrapper)
+NI_SYSCALL /* old lstat syscall */
+SYSCALL(sys_readlink,sys_readlink,sys32_readlink_wrapper) /* 85 */
+SYSCALL(sys_uselib,sys_uselib,sys32_uselib_wrapper)
+SYSCALL(sys_swapon,sys_swapon,sys32_swapon_wrapper)
+SYSCALL(sys_reboot,sys_reboot,sys32_reboot_wrapper)
+SYSCALL(sys_ni_syscall,sys_ni_syscall,old32_readdir_wrapper) /* old readdir syscall */
+SYSCALL(sys_old_mmap,sys_old_mmap,old32_mmap_wrapper) /* 90 */
+SYSCALL(sys_munmap,sys_munmap,sys32_munmap_wrapper)
+SYSCALL(sys_truncate,sys_truncate,sys32_truncate_wrapper)
+SYSCALL(sys_ftruncate,sys_ftruncate,sys32_ftruncate_wrapper)
+SYSCALL(sys_fchmod,sys_fchmod,sys32_fchmod_wrapper)
+SYSCALL(sys_fchown16,sys_ni_syscall,sys32_fchown16_wrapper) /* 95 old fchown16 syscall*/
+SYSCALL(sys_getpriority,sys_getpriority,sys32_getpriority_wrapper)
+SYSCALL(sys_setpriority,sys_setpriority,sys32_setpriority_wrapper)
+NI_SYSCALL /* old profil syscall */
+SYSCALL(sys_statfs,sys_statfs,compat_sys_statfs_wrapper)
+SYSCALL(sys_fstatfs,sys_fstatfs,compat_sys_fstatfs_wrapper) /* 100 */
+NI_SYSCALL /* ioperm for i386 */
+SYSCALL(sys_socketcall,sys_socketcall,compat_sys_socketcall_wrapper)
+SYSCALL(sys_syslog,sys_syslog,sys32_syslog_wrapper)
+SYSCALL(sys_setitimer,sys_setitimer,compat_sys_setitimer_wrapper)
+SYSCALL(sys_getitimer,sys_getitimer,compat_sys_getitimer_wrapper) /* 105 */
+SYSCALL(sys_newstat,sys_newstat,compat_sys_newstat_wrapper)
+SYSCALL(sys_newlstat,sys_newlstat,compat_sys_newlstat_wrapper)
+SYSCALL(sys_newfstat,sys_newfstat,compat_sys_newfstat_wrapper)
+NI_SYSCALL /* old uname syscall */
+SYSCALL(sys_lookup_dcookie,sys_lookup_dcookie,sys32_lookup_dcookie_wrapper) /* 110 */
+SYSCALL(sys_vhangup,sys_vhangup,sys_vhangup)
+NI_SYSCALL /* old "idle" system call */
+NI_SYSCALL /* vm86old for i386 */
+SYSCALL(sys_wait4,sys_wait4,compat_sys_wait4_wrapper)
+SYSCALL(sys_swapoff,sys_swapoff,sys32_swapoff_wrapper) /* 115 */
+SYSCALL(sys_sysinfo,sys_sysinfo,compat_sys_sysinfo_wrapper)
+SYSCALL(sys_s390_ipc,sys_s390_ipc,sys32_ipc_wrapper)
+SYSCALL(sys_fsync,sys_fsync,sys32_fsync_wrapper)
+SYSCALL(sys_sigreturn,sys_sigreturn,sys32_sigreturn)
+SYSCALL(sys_clone,sys_clone,sys_clone_wrapper) /* 120 */
+SYSCALL(sys_setdomainname,sys_setdomainname,sys32_setdomainname_wrapper)
+SYSCALL(sys_newuname,sys_newuname,sys32_newuname_wrapper)
+NI_SYSCALL /* modify_ldt for i386 */
+SYSCALL(sys_adjtimex,sys_adjtimex,compat_sys_adjtimex_wrapper)
+SYSCALL(sys_mprotect,sys_mprotect,sys32_mprotect_wrapper) /* 125 */
+SYSCALL(sys_sigprocmask,sys_sigprocmask,compat_sys_sigprocmask_wrapper)
+NI_SYSCALL /* old "create module" */
+SYSCALL(sys_init_module,sys_init_module,sys_init_module_wrapper)
+SYSCALL(sys_delete_module,sys_delete_module,sys_delete_module_wrapper)
+NI_SYSCALL /* 130: old get_kernel_syms */
+SYSCALL(sys_quotactl,sys_quotactl,sys32_quotactl_wrapper)
+SYSCALL(sys_getpgid,sys_getpgid,sys32_getpgid_wrapper)
+SYSCALL(sys_fchdir,sys_fchdir,sys32_fchdir_wrapper)
+SYSCALL(sys_bdflush,sys_bdflush,sys32_bdflush_wrapper)
+SYSCALL(sys_sysfs,sys_sysfs,sys32_sysfs_wrapper) /* 135 */
+SYSCALL(sys_personality,sys_s390_personality,sys32_personality_wrapper)
+NI_SYSCALL /* for afs_syscall */
+SYSCALL(sys_setfsuid16,sys_ni_syscall,sys32_setfsuid16_wrapper) /* old setfsuid16 syscall */
+SYSCALL(sys_setfsgid16,sys_ni_syscall,sys32_setfsgid16_wrapper) /* old setfsgid16 syscall */
+SYSCALL(sys_llseek,sys_llseek,sys32_llseek_wrapper) /* 140 */
+SYSCALL(sys_getdents,sys_getdents,sys32_getdents_wrapper)
+SYSCALL(sys_select,sys_select,compat_sys_select_wrapper)
+SYSCALL(sys_flock,sys_flock,sys32_flock_wrapper)
+SYSCALL(sys_msync,sys_msync,sys32_msync_wrapper)
+SYSCALL(sys_readv,sys_readv,compat_sys_readv_wrapper) /* 145 */
+SYSCALL(sys_writev,sys_writev,compat_sys_writev_wrapper)
+SYSCALL(sys_getsid,sys_getsid,sys32_getsid_wrapper)
+SYSCALL(sys_fdatasync,sys_fdatasync,sys32_fdatasync_wrapper)
+SYSCALL(sys_sysctl,sys_sysctl,sys32_sysctl_wrapper)
+SYSCALL(sys_mlock,sys_mlock,sys32_mlock_wrapper) /* 150 */
+SYSCALL(sys_munlock,sys_munlock,sys32_munlock_wrapper)
+SYSCALL(sys_mlockall,sys_mlockall,sys32_mlockall_wrapper)
+SYSCALL(sys_munlockall,sys_munlockall,sys_munlockall)
+SYSCALL(sys_sched_setparam,sys_sched_setparam,sys32_sched_setparam_wrapper)
+SYSCALL(sys_sched_getparam,sys_sched_getparam,sys32_sched_getparam_wrapper) /* 155 */
+SYSCALL(sys_sched_setscheduler,sys_sched_setscheduler,sys32_sched_setscheduler_wrapper)
+SYSCALL(sys_sched_getscheduler,sys_sched_getscheduler,sys32_sched_getscheduler_wrapper)
+SYSCALL(sys_sched_yield,sys_sched_yield,sys_sched_yield)
+SYSCALL(sys_sched_get_priority_max,sys_sched_get_priority_max,sys32_sched_get_priority_max_wrapper)
+SYSCALL(sys_sched_get_priority_min,sys_sched_get_priority_min,sys32_sched_get_priority_min_wrapper) /* 160 */
+SYSCALL(sys_sched_rr_get_interval,sys_sched_rr_get_interval,sys32_sched_rr_get_interval_wrapper)
+SYSCALL(sys_nanosleep,sys_nanosleep,compat_sys_nanosleep_wrapper)
+SYSCALL(sys_mremap,sys_mremap,sys32_mremap_wrapper)
+SYSCALL(sys_setresuid16,sys_ni_syscall,sys32_setresuid16_wrapper) /* old setresuid16 syscall */
+SYSCALL(sys_getresuid16,sys_ni_syscall,sys32_getresuid16_wrapper) /* 165 old getresuid16 syscall */
+NI_SYSCALL /* for vm86 */
+NI_SYSCALL /* old sys_query_module */
+SYSCALL(sys_poll,sys_poll,sys32_poll_wrapper)
+NI_SYSCALL /* old nfsservctl */
+SYSCALL(sys_setresgid16,sys_ni_syscall,sys32_setresgid16_wrapper) /* 170 old setresgid16 syscall */
+SYSCALL(sys_getresgid16,sys_ni_syscall,sys32_getresgid16_wrapper) /* old getresgid16 syscall */
+SYSCALL(sys_prctl,sys_prctl,sys32_prctl_wrapper)
+SYSCALL(sys_rt_sigreturn,sys_rt_sigreturn,sys32_rt_sigreturn)
+SYSCALL(sys_rt_sigaction,sys_rt_sigaction,sys32_rt_sigaction_wrapper)
+SYSCALL(sys_rt_sigprocmask,sys_rt_sigprocmask,sys32_rt_sigprocmask_wrapper) /* 175 */
+SYSCALL(sys_rt_sigpending,sys_rt_sigpending,sys32_rt_sigpending_wrapper)
+SYSCALL(sys_rt_sigtimedwait,sys_rt_sigtimedwait,compat_sys_rt_sigtimedwait_wrapper)
+SYSCALL(sys_rt_sigqueueinfo,sys_rt_sigqueueinfo,sys32_rt_sigqueueinfo_wrapper)
+SYSCALL(sys_rt_sigsuspend,sys_rt_sigsuspend,compat_sys_rt_sigsuspend_wrapper)
+SYSCALL(sys_pread64,sys_pread64,sys32_pread64_wrapper) /* 180 */
+SYSCALL(sys_pwrite64,sys_pwrite64,sys32_pwrite64_wrapper)
+SYSCALL(sys_chown16,sys_ni_syscall,sys32_chown16_wrapper) /* old chown16 syscall */
+SYSCALL(sys_getcwd,sys_getcwd,sys32_getcwd_wrapper)
+SYSCALL(sys_capget,sys_capget,sys32_capget_wrapper)
+SYSCALL(sys_capset,sys_capset,sys32_capset_wrapper) /* 185 */
+SYSCALL(sys_sigaltstack,sys_sigaltstack,sys32_sigaltstack_wrapper)
+SYSCALL(sys_sendfile,sys_sendfile64,sys32_sendfile_wrapper)
+NI_SYSCALL /* streams1 */
+NI_SYSCALL /* streams2 */
+SYSCALL(sys_vfork,sys_vfork,sys_vfork) /* 190 */
+SYSCALL(sys_getrlimit,sys_getrlimit,compat_sys_getrlimit_wrapper)
+SYSCALL(sys_mmap2,sys_mmap2,sys32_mmap2_wrapper)
+SYSCALL(sys_truncate64,sys_ni_syscall,sys32_truncate64_wrapper)
+SYSCALL(sys_ftruncate64,sys_ni_syscall,sys32_ftruncate64_wrapper)
+SYSCALL(sys_stat64,sys_ni_syscall,sys32_stat64_wrapper) /* 195 */
+SYSCALL(sys_lstat64,sys_ni_syscall,sys32_lstat64_wrapper)
+SYSCALL(sys_fstat64,sys_ni_syscall,sys32_fstat64_wrapper)
+SYSCALL(sys_lchown,sys_lchown,sys32_lchown_wrapper)
+SYSCALL(sys_getuid,sys_getuid,sys_getuid)
+SYSCALL(sys_getgid,sys_getgid,sys_getgid) /* 200 */
+SYSCALL(sys_geteuid,sys_geteuid,sys_geteuid)
+SYSCALL(sys_getegid,sys_getegid,sys_getegid)
+SYSCALL(sys_setreuid,sys_setreuid,sys32_setreuid_wrapper)
+SYSCALL(sys_setregid,sys_setregid,sys32_setregid_wrapper)
+SYSCALL(sys_getgroups,sys_getgroups,sys32_getgroups_wrapper) /* 205 */
+SYSCALL(sys_setgroups,sys_setgroups,sys32_setgroups_wrapper)
+SYSCALL(sys_fchown,sys_fchown,sys32_fchown_wrapper)
+SYSCALL(sys_setresuid,sys_setresuid,sys32_setresuid_wrapper)
+SYSCALL(sys_getresuid,sys_getresuid,sys32_getresuid_wrapper)
+SYSCALL(sys_setresgid,sys_setresgid,sys32_setresgid_wrapper) /* 210 */
+SYSCALL(sys_getresgid,sys_getresgid,sys32_getresgid_wrapper)
+SYSCALL(sys_chown,sys_chown,sys32_chown_wrapper)
+SYSCALL(sys_setuid,sys_setuid,sys32_setuid_wrapper)
+SYSCALL(sys_setgid,sys_setgid,sys32_setgid_wrapper)
+SYSCALL(sys_setfsuid,sys_setfsuid,sys32_setfsuid_wrapper) /* 215 */
+SYSCALL(sys_setfsgid,sys_setfsgid,sys32_setfsgid_wrapper)
+SYSCALL(sys_pivot_root,sys_pivot_root,sys32_pivot_root_wrapper)
+SYSCALL(sys_mincore,sys_mincore,sys32_mincore_wrapper)
+SYSCALL(sys_madvise,sys_madvise,sys32_madvise_wrapper)
+SYSCALL(sys_getdents64,sys_getdents64,sys32_getdents64_wrapper) /* 220 */
+SYSCALL(sys_fcntl64,sys_ni_syscall,compat_sys_fcntl64_wrapper)
+SYSCALL(sys_readahead,sys_readahead,sys32_readahead_wrapper)
+SYSCALL(sys_sendfile64,sys_ni_syscall,sys32_sendfile64_wrapper)
+SYSCALL(sys_setxattr,sys_setxattr,sys32_setxattr_wrapper)
+SYSCALL(sys_lsetxattr,sys_lsetxattr,sys32_lsetxattr_wrapper) /* 225 */
+SYSCALL(sys_fsetxattr,sys_fsetxattr,sys32_fsetxattr_wrapper)
+SYSCALL(sys_getxattr,sys_getxattr,sys32_getxattr_wrapper)
+SYSCALL(sys_lgetxattr,sys_lgetxattr,sys32_lgetxattr_wrapper)
+SYSCALL(sys_fgetxattr,sys_fgetxattr,sys32_fgetxattr_wrapper)
+SYSCALL(sys_listxattr,sys_listxattr,sys32_listxattr_wrapper) /* 230 */
+SYSCALL(sys_llistxattr,sys_llistxattr,sys32_llistxattr_wrapper)
+SYSCALL(sys_flistxattr,sys_flistxattr,sys32_flistxattr_wrapper)
+SYSCALL(sys_removexattr,sys_removexattr,sys32_removexattr_wrapper)
+SYSCALL(sys_lremovexattr,sys_lremovexattr,sys32_lremovexattr_wrapper)
+SYSCALL(sys_fremovexattr,sys_fremovexattr,sys32_fremovexattr_wrapper) /* 235 */
+SYSCALL(sys_gettid,sys_gettid,sys_gettid)
+SYSCALL(sys_tkill,sys_tkill,sys_tkill_wrapper)
+SYSCALL(sys_futex,sys_futex,compat_sys_futex_wrapper)
+SYSCALL(sys_sched_setaffinity,sys_sched_setaffinity,sys32_sched_setaffinity_wrapper)
+SYSCALL(sys_sched_getaffinity,sys_sched_getaffinity,sys32_sched_getaffinity_wrapper) /* 240 */
+SYSCALL(sys_tgkill,sys_tgkill,sys_tgkill_wrapper)
+NI_SYSCALL /* reserved for TUX */
+SYSCALL(sys_io_setup,sys_io_setup,sys32_io_setup_wrapper)
+SYSCALL(sys_io_destroy,sys_io_destroy,sys32_io_destroy_wrapper)
+SYSCALL(sys_io_getevents,sys_io_getevents,sys32_io_getevents_wrapper) /* 245 */
+SYSCALL(sys_io_submit,sys_io_submit,sys32_io_submit_wrapper)
+SYSCALL(sys_io_cancel,sys_io_cancel,sys32_io_cancel_wrapper)
+SYSCALL(sys_exit_group,sys_exit_group,sys32_exit_group_wrapper)
+SYSCALL(sys_epoll_create,sys_epoll_create,sys_epoll_create_wrapper)
+SYSCALL(sys_epoll_ctl,sys_epoll_ctl,sys_epoll_ctl_wrapper) /* 250 */
+SYSCALL(sys_epoll_wait,sys_epoll_wait,sys_epoll_wait_wrapper)
+SYSCALL(sys_set_tid_address,sys_set_tid_address,sys32_set_tid_address_wrapper)
+SYSCALL(sys_s390_fadvise64,sys_fadvise64_64,sys32_fadvise64_wrapper)
+SYSCALL(sys_timer_create,sys_timer_create,sys32_timer_create_wrapper)
+SYSCALL(sys_timer_settime,sys_timer_settime,sys32_timer_settime_wrapper) /* 255 */
+SYSCALL(sys_timer_gettime,sys_timer_gettime,sys32_timer_gettime_wrapper)
+SYSCALL(sys_timer_getoverrun,sys_timer_getoverrun,sys32_timer_getoverrun_wrapper)
+SYSCALL(sys_timer_delete,sys_timer_delete,sys32_timer_delete_wrapper)
+SYSCALL(sys_clock_settime,sys_clock_settime,sys32_clock_settime_wrapper)
+SYSCALL(sys_clock_gettime,sys_clock_gettime,sys32_clock_gettime_wrapper) /* 260 */
+SYSCALL(sys_clock_getres,sys_clock_getres,sys32_clock_getres_wrapper)
+SYSCALL(sys_clock_nanosleep,sys_clock_nanosleep,sys32_clock_nanosleep_wrapper)
+NI_SYSCALL /* reserved for vserver */
+SYSCALL(sys_s390_fadvise64_64,sys_ni_syscall,sys32_fadvise64_64_wrapper)
+SYSCALL(sys_statfs64,sys_statfs64,compat_sys_statfs64_wrapper)
+SYSCALL(sys_fstatfs64,sys_fstatfs64,compat_sys_fstatfs64_wrapper)
+SYSCALL(sys_remap_file_pages,sys_remap_file_pages,sys32_remap_file_pages_wrapper)
+NI_SYSCALL /* 268 sys_mbind */
+NI_SYSCALL /* 269 sys_get_mempolicy */
+NI_SYSCALL /* 270 sys_set_mempolicy */
+SYSCALL(sys_mq_open,sys_mq_open,compat_sys_mq_open_wrapper)
+SYSCALL(sys_mq_unlink,sys_mq_unlink,sys32_mq_unlink_wrapper)
+SYSCALL(sys_mq_timedsend,sys_mq_timedsend,compat_sys_mq_timedsend_wrapper)
+SYSCALL(sys_mq_timedreceive,sys_mq_timedreceive,compat_sys_mq_timedreceive_wrapper)
+SYSCALL(sys_mq_notify,sys_mq_notify,compat_sys_mq_notify_wrapper) /* 275 */
+SYSCALL(sys_mq_getsetattr,sys_mq_getsetattr,compat_sys_mq_getsetattr_wrapper)
+SYSCALL(sys_kexec_load,sys_kexec_load,compat_sys_kexec_load_wrapper)
+SYSCALL(sys_add_key,sys_add_key,compat_sys_add_key_wrapper)
+SYSCALL(sys_request_key,sys_request_key,compat_sys_request_key_wrapper)
+SYSCALL(sys_keyctl,sys_keyctl,compat_sys_keyctl_wrapper) /* 280 */
+SYSCALL(sys_waitid,sys_waitid,compat_sys_waitid_wrapper)
+SYSCALL(sys_ioprio_set,sys_ioprio_set,sys_ioprio_set_wrapper)
+SYSCALL(sys_ioprio_get,sys_ioprio_get,sys_ioprio_get_wrapper)
+SYSCALL(sys_inotify_init,sys_inotify_init,sys_inotify_init)
+SYSCALL(sys_inotify_add_watch,sys_inotify_add_watch,sys_inotify_add_watch_wrapper) /* 285 */
+SYSCALL(sys_inotify_rm_watch,sys_inotify_rm_watch,sys_inotify_rm_watch_wrapper)
+NI_SYSCALL /* 287 sys_migrate_pages */
+SYSCALL(sys_openat,sys_openat,compat_sys_openat_wrapper)
+SYSCALL(sys_mkdirat,sys_mkdirat,sys_mkdirat_wrapper)
+SYSCALL(sys_mknodat,sys_mknodat,sys_mknodat_wrapper) /* 290 */
+SYSCALL(sys_fchownat,sys_fchownat,sys_fchownat_wrapper)
+SYSCALL(sys_futimesat,sys_futimesat,compat_sys_futimesat_wrapper)
+SYSCALL(sys_fstatat64,sys_newfstatat,sys32_fstatat64_wrapper)
+SYSCALL(sys_unlinkat,sys_unlinkat,sys_unlinkat_wrapper)
+SYSCALL(sys_renameat,sys_renameat,sys_renameat_wrapper) /* 295 */
+SYSCALL(sys_linkat,sys_linkat,sys_linkat_wrapper)
+SYSCALL(sys_symlinkat,sys_symlinkat,sys_symlinkat_wrapper)
+SYSCALL(sys_readlinkat,sys_readlinkat,sys_readlinkat_wrapper)
+SYSCALL(sys_fchmodat,sys_fchmodat,sys_fchmodat_wrapper)
+SYSCALL(sys_faccessat,sys_faccessat,sys_faccessat_wrapper) /* 300 */
+SYSCALL(sys_pselect6,sys_pselect6,compat_sys_pselect6_wrapper)
+SYSCALL(sys_ppoll,sys_ppoll,compat_sys_ppoll_wrapper)
+SYSCALL(sys_unshare,sys_unshare,sys_unshare_wrapper)
+SYSCALL(sys_set_robust_list,sys_set_robust_list,compat_sys_set_robust_list_wrapper)
+SYSCALL(sys_get_robust_list,sys_get_robust_list,compat_sys_get_robust_list_wrapper)
+SYSCALL(sys_splice,sys_splice,sys_splice_wrapper)
+SYSCALL(sys_sync_file_range,sys_sync_file_range,sys_sync_file_range_wrapper)
+SYSCALL(sys_tee,sys_tee,sys_tee_wrapper)
+SYSCALL(sys_vmsplice,sys_vmsplice,compat_sys_vmsplice_wrapper)
+NI_SYSCALL /* 310 sys_move_pages */
+SYSCALL(sys_getcpu,sys_getcpu,sys_getcpu_wrapper)
+SYSCALL(sys_epoll_pwait,sys_epoll_pwait,compat_sys_epoll_pwait_wrapper)
+SYSCALL(sys_utimes,sys_utimes,compat_sys_utimes_wrapper)
+SYSCALL(sys_s390_fallocate,sys_fallocate,sys_fallocate_wrapper)
+SYSCALL(sys_utimensat,sys_utimensat,compat_sys_utimensat_wrapper) /* 315 */
+SYSCALL(sys_signalfd,sys_signalfd,compat_sys_signalfd_wrapper)
+NI_SYSCALL /* 317 old sys_timer_fd */
+SYSCALL(sys_eventfd,sys_eventfd,sys_eventfd_wrapper)
+SYSCALL(sys_timerfd_create,sys_timerfd_create,sys_timerfd_create_wrapper)
+SYSCALL(sys_timerfd_settime,sys_timerfd_settime,compat_sys_timerfd_settime_wrapper) /* 320 */
+SYSCALL(sys_timerfd_gettime,sys_timerfd_gettime,compat_sys_timerfd_gettime_wrapper)
+SYSCALL(sys_signalfd4,sys_signalfd4,compat_sys_signalfd4_wrapper)
+SYSCALL(sys_eventfd2,sys_eventfd2,sys_eventfd2_wrapper)
+SYSCALL(sys_inotify_init1,sys_inotify_init1,sys_inotify_init1_wrapper)
+SYSCALL(sys_pipe2,sys_pipe2,sys_pipe2_wrapper) /* 325 */
+SYSCALL(sys_dup3,sys_dup3,sys_dup3_wrapper)
+SYSCALL(sys_epoll_create1,sys_epoll_create1,sys_epoll_create1_wrapper)
+SYSCALL(sys_preadv,sys_preadv,compat_sys_preadv_wrapper)
+SYSCALL(sys_pwritev,sys_pwritev,compat_sys_pwritev_wrapper)
+SYSCALL(sys_rt_tgsigqueueinfo,sys_rt_tgsigqueueinfo,compat_sys_rt_tgsigqueueinfo_wrapper) /* 330 */
+SYSCALL(sys_perf_event_open,sys_perf_event_open,sys_perf_event_open_wrapper)
+SYSCALL(sys_fanotify_init,sys_fanotify_init,sys_fanotify_init_wrapper)
+SYSCALL(sys_fanotify_mark,sys_fanotify_mark,sys_fanotify_mark_wrapper)
+SYSCALL(sys_prlimit64,sys_prlimit64,sys_prlimit64_wrapper)
+SYSCALL(sys_name_to_handle_at,sys_name_to_handle_at,sys_name_to_handle_at_wrapper) /* 335 */
+SYSCALL(sys_open_by_handle_at,sys_open_by_handle_at,compat_sys_open_by_handle_at_wrapper)
+SYSCALL(sys_clock_adjtime,sys_clock_adjtime,compat_sys_clock_adjtime_wrapper)
+SYSCALL(sys_syncfs,sys_syncfs,sys_syncfs_wrapper)
+SYSCALL(sys_setns,sys_setns,sys_setns_wrapper)
+SYSCALL(sys_process_vm_readv,sys_process_vm_readv,compat_sys_process_vm_readv_wrapper) /* 340 */
+SYSCALL(sys_process_vm_writev,sys_process_vm_writev,compat_sys_process_vm_writev_wrapper)
diff --git a/arch/s390/kernel/sysinfo.c b/arch/s390/kernel/sysinfo.c
new file mode 100644
index 00000000..2a94b774
--- /dev/null
+++ b/arch/s390/kernel/sysinfo.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright IBM Corp. 2001, 2009
+ * Author(s): Ulrich Weigand <Ulrich.Weigand@de.ibm.com>,
+ * Martin Schwidefsky <schwidefsky@de.ibm.com>,
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/ebcdic.h>
+#include <asm/sysinfo.h>
+#include <asm/cpcmd.h>
+#include <asm/topology.h>
+
+/* Sigh, math-emu. Don't ask. */
+#include <asm/sfp-util.h>
+#include <math-emu/soft-fp.h>
+#include <math-emu/single.h>
+
+static inline int stsi_0(void)
+{
+ int rc = stsi(NULL, 0, 0, 0);
+ return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
+}
+
+static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len)
+{
+ if (stsi(info, 1, 1, 1) == -ENOSYS)
+ return len;
+
+ EBCASC(info->manufacturer, sizeof(info->manufacturer));
+ EBCASC(info->type, sizeof(info->type));
+ EBCASC(info->model, sizeof(info->model));
+ EBCASC(info->sequence, sizeof(info->sequence));
+ EBCASC(info->plant, sizeof(info->plant));
+ EBCASC(info->model_capacity, sizeof(info->model_capacity));
+ EBCASC(info->model_perm_cap, sizeof(info->model_perm_cap));
+ EBCASC(info->model_temp_cap, sizeof(info->model_temp_cap));
+ len += sprintf(page + len, "Manufacturer: %-16.16s\n",
+ info->manufacturer);
+ len += sprintf(page + len, "Type: %-4.4s\n",
+ info->type);
+ if (info->model[0] != '\0')
+ /*
+ * Sigh: the model field has been renamed with System z9
+ * to model_capacity and a new model field has been added
+ * after the plant field. To avoid confusing older programs
+ * the "Model:" prints "model_capacity model" or just
+ * "model_capacity" if the model string is empty .
+ */
+ len += sprintf(page + len,
+ "Model: %-16.16s %-16.16s\n",
+ info->model_capacity, info->model);
+ else
+ len += sprintf(page + len, "Model: %-16.16s\n",
+ info->model_capacity);
+ len += sprintf(page + len, "Sequence Code: %-16.16s\n",
+ info->sequence);
+ len += sprintf(page + len, "Plant: %-4.4s\n",
+ info->plant);
+ len += sprintf(page + len, "Model Capacity: %-16.16s %08u\n",
+ info->model_capacity, *(u32 *) info->model_cap_rating);
+ if (info->model_perm_cap[0] != '\0')
+ len += sprintf(page + len,
+ "Model Perm. Capacity: %-16.16s %08u\n",
+ info->model_perm_cap,
+ *(u32 *) info->model_perm_cap_rating);
+ if (info->model_temp_cap[0] != '\0')
+ len += sprintf(page + len,
+ "Model Temp. Capacity: %-16.16s %08u\n",
+ info->model_temp_cap,
+ *(u32 *) info->model_temp_cap_rating);
+ if (info->cai) {
+ len += sprintf(page + len,
+ "Capacity Adj. Ind.: %d\n",
+ info->cai);
+ len += sprintf(page + len, "Capacity Ch. Reason: %d\n",
+ info->ccr);
+ }
+ return len;
+}
+
+static int stsi_15_1_x(struct sysinfo_15_1_x *info, char *page, int len)
+{
+ static int max_mnest;
+ int i, rc;
+
+ len += sprintf(page + len, "\n");
+ if (!MACHINE_HAS_TOPOLOGY)
+ return len;
+ if (max_mnest) {
+ stsi(info, 15, 1, max_mnest);
+ } else {
+ for (max_mnest = 6; max_mnest > 1; max_mnest--) {
+ rc = stsi(info, 15, 1, max_mnest);
+ if (rc != -ENOSYS)
+ break;
+ }
+ }
+ len += sprintf(page + len, "CPU Topology HW: ");
+ for (i = 0; i < TOPOLOGY_NR_MAG; i++)
+ len += sprintf(page + len, " %d", info->mag[i]);
+ len += sprintf(page + len, "\n");
+#ifdef CONFIG_SCHED_MC
+ store_topology(info);
+ len += sprintf(page + len, "CPU Topology SW: ");
+ for (i = 0; i < TOPOLOGY_NR_MAG; i++)
+ len += sprintf(page + len, " %d", info->mag[i]);
+ len += sprintf(page + len, "\n");
+#endif
+ return len;
+}
+
+static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len)
+{
+ struct sysinfo_1_2_2_extension *ext;
+ int i;
+
+ if (stsi(info, 1, 2, 2) == -ENOSYS)
+ return len;
+ ext = (struct sysinfo_1_2_2_extension *)
+ ((unsigned long) info + info->acc_offset);
+
+ len += sprintf(page + len, "CPUs Total: %d\n",
+ info->cpus_total);
+ len += sprintf(page + len, "CPUs Configured: %d\n",
+ info->cpus_configured);
+ len += sprintf(page + len, "CPUs Standby: %d\n",
+ info->cpus_standby);
+ len += sprintf(page + len, "CPUs Reserved: %d\n",
+ info->cpus_reserved);
+
+ if (info->format == 1) {
+ /*
+ * Sigh 2. According to the specification the alternate
+ * capability field is a 32 bit floating point number
+ * if the higher order 8 bits are not zero. Printing
+ * a floating point number in the kernel is a no-no,
+ * always print the number as 32 bit unsigned integer.
+ * The user-space needs to know about the strange
+ * encoding of the alternate cpu capability.
+ */
+ len += sprintf(page + len, "Capability: %u %u\n",
+ info->capability, ext->alt_capability);
+ for (i = 2; i <= info->cpus_total; i++)
+ len += sprintf(page + len,
+ "Adjustment %02d-way: %u %u\n",
+ i, info->adjustment[i-2],
+ ext->alt_adjustment[i-2]);
+
+ } else {
+ len += sprintf(page + len, "Capability: %u\n",
+ info->capability);
+ for (i = 2; i <= info->cpus_total; i++)
+ len += sprintf(page + len,
+ "Adjustment %02d-way: %u\n",
+ i, info->adjustment[i-2]);
+ }
+
+ if (info->secondary_capability != 0)
+ len += sprintf(page + len, "Secondary Capability: %d\n",
+ info->secondary_capability);
+ return len;
+}
+
+static int stsi_2_2_2(struct sysinfo_2_2_2 *info, char *page, int len)
+{
+ if (stsi(info, 2, 2, 2) == -ENOSYS)
+ return len;
+
+ EBCASC(info->name, sizeof(info->name));
+
+ len += sprintf(page + len, "\n");
+ len += sprintf(page + len, "LPAR Number: %d\n",
+ info->lpar_number);
+
+ len += sprintf(page + len, "LPAR Characteristics: ");
+ if (info->characteristics & LPAR_CHAR_DEDICATED)
+ len += sprintf(page + len, "Dedicated ");
+ if (info->characteristics & LPAR_CHAR_SHARED)
+ len += sprintf(page + len, "Shared ");
+ if (info->characteristics & LPAR_CHAR_LIMITED)
+ len += sprintf(page + len, "Limited ");
+ len += sprintf(page + len, "\n");
+
+ len += sprintf(page + len, "LPAR Name: %-8.8s\n",
+ info->name);
+
+ len += sprintf(page + len, "LPAR Adjustment: %d\n",
+ info->caf);
+
+ len += sprintf(page + len, "LPAR CPUs Total: %d\n",
+ info->cpus_total);
+ len += sprintf(page + len, "LPAR CPUs Configured: %d\n",
+ info->cpus_configured);
+ len += sprintf(page + len, "LPAR CPUs Standby: %d\n",
+ info->cpus_standby);
+ len += sprintf(page + len, "LPAR CPUs Reserved: %d\n",
+ info->cpus_reserved);
+ len += sprintf(page + len, "LPAR CPUs Dedicated: %d\n",
+ info->cpus_dedicated);
+ len += sprintf(page + len, "LPAR CPUs Shared: %d\n",
+ info->cpus_shared);
+ return len;
+}
+
+static int stsi_3_2_2(struct sysinfo_3_2_2 *info, char *page, int len)
+{
+ int i;
+
+ if (stsi(info, 3, 2, 2) == -ENOSYS)
+ return len;
+ for (i = 0; i < info->count; i++) {
+ EBCASC(info->vm[i].name, sizeof(info->vm[i].name));
+ EBCASC(info->vm[i].cpi, sizeof(info->vm[i].cpi));
+ len += sprintf(page + len, "\n");
+ len += sprintf(page + len, "VM%02d Name: %-8.8s\n",
+ i, info->vm[i].name);
+ len += sprintf(page + len, "VM%02d Control Program: %-16.16s\n",
+ i, info->vm[i].cpi);
+
+ len += sprintf(page + len, "VM%02d Adjustment: %d\n",
+ i, info->vm[i].caf);
+
+ len += sprintf(page + len, "VM%02d CPUs Total: %d\n",
+ i, info->vm[i].cpus_total);
+ len += sprintf(page + len, "VM%02d CPUs Configured: %d\n",
+ i, info->vm[i].cpus_configured);
+ len += sprintf(page + len, "VM%02d CPUs Standby: %d\n",
+ i, info->vm[i].cpus_standby);
+ len += sprintf(page + len, "VM%02d CPUs Reserved: %d\n",
+ i, info->vm[i].cpus_reserved);
+ }
+ return len;
+}
+
+static int proc_read_sysinfo(char *page, char **start,
+ off_t off, int count,
+ int *eof, void *data)
+{
+ unsigned long info = get_zeroed_page(GFP_KERNEL);
+ int level, len;
+
+ if (!info)
+ return 0;
+
+ len = 0;
+ level = stsi_0();
+ if (level >= 1)
+ len = stsi_1_1_1((struct sysinfo_1_1_1 *) info, page, len);
+
+ if (level >= 1)
+ len = stsi_15_1_x((struct sysinfo_15_1_x *) info, page, len);
+
+ if (level >= 1)
+ len = stsi_1_2_2((struct sysinfo_1_2_2 *) info, page, len);
+
+ if (level >= 2)
+ len = stsi_2_2_2((struct sysinfo_2_2_2 *) info, page, len);
+
+ if (level >= 3)
+ len = stsi_3_2_2((struct sysinfo_3_2_2 *) info, page, len);
+
+ free_page(info);
+ return len;
+}
+
+static __init int create_proc_sysinfo(void)
+{
+ create_proc_read_entry("sysinfo", 0444, NULL,
+ proc_read_sysinfo, NULL);
+ return 0;
+}
+device_initcall(create_proc_sysinfo);
+
+/*
+ * Service levels interface.
+ */
+
+static DECLARE_RWSEM(service_level_sem);
+static LIST_HEAD(service_level_list);
+
+int register_service_level(struct service_level *slr)
+{
+ struct service_level *ptr;
+
+ down_write(&service_level_sem);
+ list_for_each_entry(ptr, &service_level_list, list)
+ if (ptr == slr) {
+ up_write(&service_level_sem);
+ return -EEXIST;
+ }
+ list_add_tail(&slr->list, &service_level_list);
+ up_write(&service_level_sem);
+ return 0;
+}
+EXPORT_SYMBOL(register_service_level);
+
+int unregister_service_level(struct service_level *slr)
+{
+ struct service_level *ptr, *next;
+ int rc = -ENOENT;
+
+ down_write(&service_level_sem);
+ list_for_each_entry_safe(ptr, next, &service_level_list, list) {
+ if (ptr != slr)
+ continue;
+ list_del(&ptr->list);
+ rc = 0;
+ break;
+ }
+ up_write(&service_level_sem);
+ return rc;
+}
+EXPORT_SYMBOL(unregister_service_level);
+
+static void *service_level_start(struct seq_file *m, loff_t *pos)
+{
+ down_read(&service_level_sem);
+ return seq_list_start(&service_level_list, *pos);
+}
+
+static void *service_level_next(struct seq_file *m, void *p, loff_t *pos)
+{
+ return seq_list_next(p, &service_level_list, pos);
+}
+
+static void service_level_stop(struct seq_file *m, void *p)
+{
+ up_read(&service_level_sem);
+}
+
+static int service_level_show(struct seq_file *m, void *p)
+{
+ struct service_level *slr;
+
+ slr = list_entry(p, struct service_level, list);
+ slr->seq_print(m, slr);
+ return 0;
+}
+
+static const struct seq_operations service_level_seq_ops = {
+ .start = service_level_start,
+ .next = service_level_next,
+ .stop = service_level_stop,
+ .show = service_level_show
+};
+
+static int service_level_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &service_level_seq_ops);
+}
+
+static const struct file_operations service_level_ops = {
+ .open = service_level_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release
+};
+
+static void service_level_vm_print(struct seq_file *m,
+ struct service_level *slr)
+{
+ char *query_buffer, *str;
+
+ query_buffer = kmalloc(1024, GFP_KERNEL | GFP_DMA);
+ if (!query_buffer)
+ return;
+ cpcmd("QUERY CPLEVEL", query_buffer, 1024, NULL);
+ str = strchr(query_buffer, '\n');
+ if (str)
+ *str = 0;
+ seq_printf(m, "VM: %s\n", query_buffer);
+ kfree(query_buffer);
+}
+
+static struct service_level service_level_vm = {
+ .seq_print = service_level_vm_print
+};
+
+static __init int create_proc_service_level(void)
+{
+ proc_create("service_levels", 0, NULL, &service_level_ops);
+ if (MACHINE_IS_VM)
+ register_service_level(&service_level_vm);
+ return 0;
+}
+subsys_initcall(create_proc_service_level);
+
+/*
+ * Bogomips calculation based on cpu capability.
+ */
+int get_cpu_capability(unsigned int *capability)
+{
+ struct sysinfo_1_2_2 *info;
+ int rc;
+
+ info = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ rc = stsi(info, 1, 2, 2);
+ if (rc == -ENOSYS)
+ goto out;
+ rc = 0;
+ *capability = info->capability;
+out:
+ free_page((unsigned long) info);
+ return rc;
+}
+
+/*
+ * CPU capability might have changed. Therefore recalculate loops_per_jiffy.
+ */
+void s390_adjust_jiffies(void)
+{
+ struct sysinfo_1_2_2 *info;
+ const unsigned int fmil = 0x4b189680; /* 1e7 as 32-bit float. */
+ FP_DECL_S(SA); FP_DECL_S(SB); FP_DECL_S(SR);
+ FP_DECL_EX;
+ unsigned int capability;
+
+ info = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!info)
+ return;
+
+ if (stsi(info, 1, 2, 2) != -ENOSYS) {
+ /*
+ * Major sigh. The cpu capability encoding is "special".
+ * If the first 9 bits of info->capability are 0 then it
+ * is a 32 bit unsigned integer in the range 0 .. 2^23.
+ * If the first 9 bits are != 0 then it is a 32 bit float.
+ * In addition a lower value indicates a proportionally
+ * higher cpu capacity. Bogomips are the other way round.
+ * To get to a halfway suitable number we divide 1e7
+ * by the cpu capability number. Yes, that means a floating
+ * point division .. math-emu here we come :-)
+ */
+ FP_UNPACK_SP(SA, &fmil);
+ if ((info->capability >> 23) == 0)
+ FP_FROM_INT_S(SB, (long) info->capability, 64, long);
+ else
+ FP_UNPACK_SP(SB, &info->capability);
+ FP_DIV_S(SR, SA, SB);
+ FP_TO_INT_S(capability, SR, 32, 0);
+ } else
+ /*
+ * Really old machine without stsi block for basic
+ * cpu information. Report 42.0 bogomips.
+ */
+ capability = 42;
+ loops_per_jiffy = capability * (500000/HZ);
+ free_page((unsigned long) info);
+}
+
+/*
+ * calibrate the delay loop
+ */
+void __cpuinit calibrate_delay(void)
+{
+ s390_adjust_jiffies();
+ /* Print the good old Bogomips line .. */
+ printk(KERN_DEBUG "Calibrating delay loop (skipped)... "
+ "%lu.%02lu BogoMIPS preset\n", loops_per_jiffy/(500000/HZ),
+ (loops_per_jiffy/(5000/HZ)) % 100);
+}
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
new file mode 100644
index 00000000..d4e1cb1d
--- /dev/null
+++ b/arch/s390/kernel/time.c
@@ -0,0 +1,1789 @@
+/*
+ * arch/s390/kernel/time.c
+ * Time of day based timer functions.
+ *
+ * S390 version
+ * Copyright IBM Corp. 1999, 2008
+ * Author(s): Hartmut Penner (hp@de.ibm.com),
+ * Martin Schwidefsky (schwidefsky@de.ibm.com),
+ * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
+ *
+ * Derived from "arch/i386/kernel/time.c"
+ * Copyright (C) 1991, 1992, 1995 Linus Torvalds
+ */
+
+#define KMSG_COMPONENT "time"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/kernel_stat.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/cpu.h>
+#include <linux/stop_machine.h>
+#include <linux/time.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+#include <linux/profile.h>
+#include <linux/timex.h>
+#include <linux/notifier.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <linux/gfp.h>
+#include <linux/kprobes.h>
+#include <asm/uaccess.h>
+#include <asm/delay.h>
+#include <asm/div64.h>
+#include <asm/vdso.h>
+#include <asm/irq.h>
+#include <asm/irq_regs.h>
+#include <asm/timer.h>
+#include <asm/etr.h>
+#include <asm/cio.h>
+#include "entry.h"
+
+/* change this if you have some constant time drift */
+#define USECS_PER_JIFFY ((unsigned long) 1000000/HZ)
+#define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
+
+u64 sched_clock_base_cc = -1; /* Force to data section. */
+EXPORT_SYMBOL_GPL(sched_clock_base_cc);
+
+static DEFINE_PER_CPU(struct clock_event_device, comparators);
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long notrace __kprobes sched_clock(void)
+{
+ return (get_clock_monotonic() * 125) >> 9;
+}
+
+/*
+ * Monotonic_clock - returns # of nanoseconds passed since time_init()
+ */
+unsigned long long monotonic_clock(void)
+{
+ return sched_clock();
+}
+EXPORT_SYMBOL(monotonic_clock);
+
+void tod_to_timeval(__u64 todval, struct timespec *xt)
+{
+ unsigned long long sec;
+
+ sec = todval >> 12;
+ do_div(sec, 1000000);
+ xt->tv_sec = sec;
+ todval -= (sec * 1000000) << 12;
+ xt->tv_nsec = ((todval * 1000) >> 12);
+}
+EXPORT_SYMBOL(tod_to_timeval);
+
+void clock_comparator_work(void)
+{
+ struct clock_event_device *cd;
+
+ S390_lowcore.clock_comparator = -1ULL;
+ set_clock_comparator(S390_lowcore.clock_comparator);
+ cd = &__get_cpu_var(comparators);
+ cd->event_handler(cd);
+}
+
+/*
+ * Fixup the clock comparator.
+ */
+static void fixup_clock_comparator(unsigned long long delta)
+{
+ /* If nobody is waiting there's nothing to fix. */
+ if (S390_lowcore.clock_comparator == -1ULL)
+ return;
+ S390_lowcore.clock_comparator += delta;
+ set_clock_comparator(S390_lowcore.clock_comparator);
+}
+
+static int s390_next_ktime(ktime_t expires,
+ struct clock_event_device *evt)
+{
+ struct timespec ts;
+ u64 nsecs;
+
+ ts.tv_sec = ts.tv_nsec = 0;
+ monotonic_to_bootbased(&ts);
+ nsecs = ktime_to_ns(ktime_add(timespec_to_ktime(ts), expires));
+ do_div(nsecs, 125);
+ S390_lowcore.clock_comparator = sched_clock_base_cc + (nsecs << 9);
+ set_clock_comparator(S390_lowcore.clock_comparator);
+ return 0;
+}
+
+static void s390_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+}
+
+/*
+ * Set up lowcore and control register of the current cpu to
+ * enable TOD clock and clock comparator interrupts.
+ */
+void init_cpu_timer(void)
+{
+ struct clock_event_device *cd;
+ int cpu;
+
+ S390_lowcore.clock_comparator = -1ULL;
+ set_clock_comparator(S390_lowcore.clock_comparator);
+
+ cpu = smp_processor_id();
+ cd = &per_cpu(comparators, cpu);
+ cd->name = "comparator";
+ cd->features = CLOCK_EVT_FEAT_ONESHOT |
+ CLOCK_EVT_FEAT_KTIME;
+ cd->mult = 16777;
+ cd->shift = 12;
+ cd->min_delta_ns = 1;
+ cd->max_delta_ns = LONG_MAX;
+ cd->rating = 400;
+ cd->cpumask = cpumask_of(cpu);
+ cd->set_next_ktime = s390_next_ktime;
+ cd->set_mode = s390_set_mode;
+
+ clockevents_register_device(cd);
+
+ /* Enable clock comparator timer interrupt. */
+ __ctl_set_bit(0,11);
+
+ /* Always allow the timing alert external interrupt. */
+ __ctl_set_bit(0, 4);
+}
+
+static void clock_comparator_interrupt(struct ext_code ext_code,
+ unsigned int param32,
+ unsigned long param64)
+{
+ kstat_cpu(smp_processor_id()).irqs[EXTINT_CLK]++;
+ if (S390_lowcore.clock_comparator == -1ULL)
+ set_clock_comparator(S390_lowcore.clock_comparator);
+}
+
+static void etr_timing_alert(struct etr_irq_parm *);
+static void stp_timing_alert(struct stp_irq_parm *);
+
+static void timing_alert_interrupt(struct ext_code ext_code,
+ unsigned int param32, unsigned long param64)
+{
+ kstat_cpu(smp_processor_id()).irqs[EXTINT_TLA]++;
+ if (param32 & 0x00c40000)
+ etr_timing_alert((struct etr_irq_parm *) &param32);
+ if (param32 & 0x00038000)
+ stp_timing_alert((struct stp_irq_parm *) &param32);
+}
+
+static void etr_reset(void);
+static void stp_reset(void);
+
+void read_persistent_clock(struct timespec *ts)
+{
+ tod_to_timeval(get_clock() - TOD_UNIX_EPOCH, ts);
+}
+
+void read_boot_clock(struct timespec *ts)
+{
+ tod_to_timeval(sched_clock_base_cc - TOD_UNIX_EPOCH, ts);
+}
+
+static cycle_t read_tod_clock(struct clocksource *cs)
+{
+ return get_clock();
+}
+
+static struct clocksource clocksource_tod = {
+ .name = "tod",
+ .rating = 400,
+ .read = read_tod_clock,
+ .mask = -1ULL,
+ .mult = 1000,
+ .shift = 12,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+struct clocksource * __init clocksource_default_clock(void)
+{
+ return &clocksource_tod;
+}
+
+void update_vsyscall(struct timespec *wall_time, struct timespec *wtm,
+ struct clocksource *clock, u32 mult)
+{
+ if (clock != &clocksource_tod)
+ return;
+
+ /* Make userspace gettimeofday spin until we're done. */
+ ++vdso_data->tb_update_count;
+ smp_wmb();
+ vdso_data->xtime_tod_stamp = clock->cycle_last;
+ vdso_data->xtime_clock_sec = wall_time->tv_sec;
+ vdso_data->xtime_clock_nsec = wall_time->tv_nsec;
+ vdso_data->wtom_clock_sec = wtm->tv_sec;
+ vdso_data->wtom_clock_nsec = wtm->tv_nsec;
+ vdso_data->ntp_mult = mult;
+ smp_wmb();
+ ++vdso_data->tb_update_count;
+}
+
+extern struct timezone sys_tz;
+
+void update_vsyscall_tz(void)
+{
+ /* Make userspace gettimeofday spin until we're done. */
+ ++vdso_data->tb_update_count;
+ smp_wmb();
+ vdso_data->tz_minuteswest = sys_tz.tz_minuteswest;
+ vdso_data->tz_dsttime = sys_tz.tz_dsttime;
+ smp_wmb();
+ ++vdso_data->tb_update_count;
+}
+
+/*
+ * Initialize the TOD clock and the CPU timer of
+ * the boot cpu.
+ */
+void __init time_init(void)
+{
+ /* Reset time synchronization interfaces. */
+ etr_reset();
+ stp_reset();
+
+ /* request the clock comparator external interrupt */
+ if (register_external_interrupt(0x1004, clock_comparator_interrupt))
+ panic("Couldn't request external interrupt 0x1004");
+
+ /* request the timing alert external interrupt */
+ if (register_external_interrupt(0x1406, timing_alert_interrupt))
+ panic("Couldn't request external interrupt 0x1406");
+
+ if (clocksource_register(&clocksource_tod) != 0)
+ panic("Could not register TOD clock source");
+
+ /* Enable TOD clock interrupts on the boot cpu. */
+ init_cpu_timer();
+
+ /* Enable cpu timer interrupts on the boot cpu. */
+ vtime_init();
+}
+
+/*
+ * The time is "clock". old is what we think the time is.
+ * Adjust the value by a multiple of jiffies and add the delta to ntp.
+ * "delay" is an approximation how long the synchronization took. If
+ * the time correction is positive, then "delay" is subtracted from
+ * the time difference and only the remaining part is passed to ntp.
+ */
+static unsigned long long adjust_time(unsigned long long old,
+ unsigned long long clock,
+ unsigned long long delay)
+{
+ unsigned long long delta, ticks;
+ struct timex adjust;
+
+ if (clock > old) {
+ /* It is later than we thought. */
+ delta = ticks = clock - old;
+ delta = ticks = (delta < delay) ? 0 : delta - delay;
+ delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
+ adjust.offset = ticks * (1000000 / HZ);
+ } else {
+ /* It is earlier than we thought. */
+ delta = ticks = old - clock;
+ delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
+ delta = -delta;
+ adjust.offset = -ticks * (1000000 / HZ);
+ }
+ sched_clock_base_cc += delta;
+ if (adjust.offset != 0) {
+ pr_notice("The ETR interface has adjusted the clock "
+ "by %li microseconds\n", adjust.offset);
+ adjust.modes = ADJ_OFFSET_SINGLESHOT;
+ do_adjtimex(&adjust);
+ }
+ return delta;
+}
+
+static DEFINE_PER_CPU(atomic_t, clock_sync_word);
+static DEFINE_MUTEX(clock_sync_mutex);
+static unsigned long clock_sync_flags;
+
+#define CLOCK_SYNC_HAS_ETR 0
+#define CLOCK_SYNC_HAS_STP 1
+#define CLOCK_SYNC_ETR 2
+#define CLOCK_SYNC_STP 3
+
+/*
+ * The synchronous get_clock function. It will write the current clock
+ * value to the clock pointer and return 0 if the clock is in sync with
+ * the external time source. If the clock mode is local it will return
+ * -ENOSYS and -EAGAIN if the clock is not in sync with the external
+ * reference.
+ */
+int get_sync_clock(unsigned long long *clock)
+{
+ atomic_t *sw_ptr;
+ unsigned int sw0, sw1;
+
+ sw_ptr = &get_cpu_var(clock_sync_word);
+ sw0 = atomic_read(sw_ptr);
+ *clock = get_clock();
+ sw1 = atomic_read(sw_ptr);
+ put_cpu_var(clock_sync_word);
+ if (sw0 == sw1 && (sw0 & 0x80000000U))
+ /* Success: time is in sync. */
+ return 0;
+ if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags) &&
+ !test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
+ return -ENOSYS;
+ if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags) &&
+ !test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
+ return -EACCES;
+ return -EAGAIN;
+}
+EXPORT_SYMBOL(get_sync_clock);
+
+/*
+ * Make get_sync_clock return -EAGAIN.
+ */
+static void disable_sync_clock(void *dummy)
+{
+ atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word);
+ /*
+ * Clear the in-sync bit 2^31. All get_sync_clock calls will
+ * fail until the sync bit is turned back on. In addition
+ * increase the "sequence" counter to avoid the race of an
+ * etr event and the complete recovery against get_sync_clock.
+ */
+ atomic_clear_mask(0x80000000, sw_ptr);
+ atomic_inc(sw_ptr);
+}
+
+/*
+ * Make get_sync_clock return 0 again.
+ * Needs to be called from a context disabled for preemption.
+ */
+static void enable_sync_clock(void)
+{
+ atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word);
+ atomic_set_mask(0x80000000, sw_ptr);
+}
+
+/*
+ * Function to check if the clock is in sync.
+ */
+static inline int check_sync_clock(void)
+{
+ atomic_t *sw_ptr;
+ int rc;
+
+ sw_ptr = &get_cpu_var(clock_sync_word);
+ rc = (atomic_read(sw_ptr) & 0x80000000U) != 0;
+ put_cpu_var(clock_sync_word);
+ return rc;
+}
+
+/* Single threaded workqueue used for etr and stp sync events */
+static struct workqueue_struct *time_sync_wq;
+
+static void __init time_init_wq(void)
+{
+ if (time_sync_wq)
+ return;
+ time_sync_wq = create_singlethread_workqueue("timesync");
+}
+
+/*
+ * External Time Reference (ETR) code.
+ */
+static int etr_port0_online;
+static int etr_port1_online;
+static int etr_steai_available;
+
+static int __init early_parse_etr(char *p)
+{
+ if (strncmp(p, "off", 3) == 0)
+ etr_port0_online = etr_port1_online = 0;
+ else if (strncmp(p, "port0", 5) == 0)
+ etr_port0_online = 1;
+ else if (strncmp(p, "port1", 5) == 0)
+ etr_port1_online = 1;
+ else if (strncmp(p, "on", 2) == 0)
+ etr_port0_online = etr_port1_online = 1;
+ return 0;
+}
+early_param("etr", early_parse_etr);
+
+enum etr_event {
+ ETR_EVENT_PORT0_CHANGE,
+ ETR_EVENT_PORT1_CHANGE,
+ ETR_EVENT_PORT_ALERT,
+ ETR_EVENT_SYNC_CHECK,
+ ETR_EVENT_SWITCH_LOCAL,
+ ETR_EVENT_UPDATE,
+};
+
+/*
+ * Valid bit combinations of the eacr register are (x = don't care):
+ * e0 e1 dp p0 p1 ea es sl
+ * 0 0 x 0 0 0 0 0 initial, disabled state
+ * 0 0 x 0 1 1 0 0 port 1 online
+ * 0 0 x 1 0 1 0 0 port 0 online
+ * 0 0 x 1 1 1 0 0 both ports online
+ * 0 1 x 0 1 1 0 0 port 1 online and usable, ETR or PPS mode
+ * 0 1 x 0 1 1 0 1 port 1 online, usable and ETR mode
+ * 0 1 x 0 1 1 1 0 port 1 online, usable, PPS mode, in-sync
+ * 0 1 x 0 1 1 1 1 port 1 online, usable, ETR mode, in-sync
+ * 0 1 x 1 1 1 0 0 both ports online, port 1 usable
+ * 0 1 x 1 1 1 1 0 both ports online, port 1 usable, PPS mode, in-sync
+ * 0 1 x 1 1 1 1 1 both ports online, port 1 usable, ETR mode, in-sync
+ * 1 0 x 1 0 1 0 0 port 0 online and usable, ETR or PPS mode
+ * 1 0 x 1 0 1 0 1 port 0 online, usable and ETR mode
+ * 1 0 x 1 0 1 1 0 port 0 online, usable, PPS mode, in-sync
+ * 1 0 x 1 0 1 1 1 port 0 online, usable, ETR mode, in-sync
+ * 1 0 x 1 1 1 0 0 both ports online, port 0 usable
+ * 1 0 x 1 1 1 1 0 both ports online, port 0 usable, PPS mode, in-sync
+ * 1 0 x 1 1 1 1 1 both ports online, port 0 usable, ETR mode, in-sync
+ * 1 1 x 1 1 1 1 0 both ports online & usable, ETR, in-sync
+ * 1 1 x 1 1 1 1 1 both ports online & usable, ETR, in-sync
+ */
+static struct etr_eacr etr_eacr;
+static u64 etr_tolec; /* time of last eacr update */
+static struct etr_aib etr_port0;
+static int etr_port0_uptodate;
+static struct etr_aib etr_port1;
+static int etr_port1_uptodate;
+static unsigned long etr_events;
+static struct timer_list etr_timer;
+
+static void etr_timeout(unsigned long dummy);
+static void etr_work_fn(struct work_struct *work);
+static DEFINE_MUTEX(etr_work_mutex);
+static DECLARE_WORK(etr_work, etr_work_fn);
+
+/*
+ * Reset ETR attachment.
+ */
+static void etr_reset(void)
+{
+ etr_eacr = (struct etr_eacr) {
+ .e0 = 0, .e1 = 0, ._pad0 = 4, .dp = 0,
+ .p0 = 0, .p1 = 0, ._pad1 = 0, .ea = 0,
+ .es = 0, .sl = 0 };
+ if (etr_setr(&etr_eacr) == 0) {
+ etr_tolec = get_clock();
+ set_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags);
+ if (etr_port0_online && etr_port1_online)
+ set_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
+ } else if (etr_port0_online || etr_port1_online) {
+ pr_warning("The real or virtual hardware system does "
+ "not provide an ETR interface\n");
+ etr_port0_online = etr_port1_online = 0;
+ }
+}
+
+static int __init etr_init(void)
+{
+ struct etr_aib aib;
+
+ if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags))
+ return 0;
+ time_init_wq();
+ /* Check if this machine has the steai instruction. */
+ if (etr_steai(&aib, ETR_STEAI_STEPPING_PORT) == 0)
+ etr_steai_available = 1;
+ setup_timer(&etr_timer, etr_timeout, 0UL);
+ if (etr_port0_online) {
+ set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
+ queue_work(time_sync_wq, &etr_work);
+ }
+ if (etr_port1_online) {
+ set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);
+ queue_work(time_sync_wq, &etr_work);
+ }
+ return 0;
+}
+
+arch_initcall(etr_init);
+
+/*
+ * Two sorts of ETR machine checks. The architecture reads:
+ * "When a machine-check niterruption occurs and if a switch-to-local or
+ * ETR-sync-check interrupt request is pending but disabled, this pending
+ * disabled interruption request is indicated and is cleared".
+ * Which means that we can get etr_switch_to_local events from the machine
+ * check handler although the interruption condition is disabled. Lovely..
+ */
+
+/*
+ * Switch to local machine check. This is called when the last usable
+ * ETR port goes inactive. After switch to local the clock is not in sync.
+ */
+void etr_switch_to_local(void)
+{
+ if (!etr_eacr.sl)
+ return;
+ disable_sync_clock(NULL);
+ if (!test_and_set_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events)) {
+ etr_eacr.es = etr_eacr.sl = 0;
+ etr_setr(&etr_eacr);
+ queue_work(time_sync_wq, &etr_work);
+ }
+}
+
+/*
+ * ETR sync check machine check. This is called when the ETR OTE and the
+ * local clock OTE are farther apart than the ETR sync check tolerance.
+ * After a ETR sync check the clock is not in sync. The machine check
+ * is broadcasted to all cpus at the same time.
+ */
+void etr_sync_check(void)
+{
+ if (!etr_eacr.es)
+ return;
+ disable_sync_clock(NULL);
+ if (!test_and_set_bit(ETR_EVENT_SYNC_CHECK, &etr_events)) {
+ etr_eacr.es = 0;
+ etr_setr(&etr_eacr);
+ queue_work(time_sync_wq, &etr_work);
+ }
+}
+
+/*
+ * ETR timing alert. There are two causes:
+ * 1) port state change, check the usability of the port
+ * 2) port alert, one of the ETR-data-validity bits (v1-v2 bits of the
+ * sldr-status word) or ETR-data word 1 (edf1) or ETR-data word 3 (edf3)
+ * or ETR-data word 4 (edf4) has changed.
+ */
+static void etr_timing_alert(struct etr_irq_parm *intparm)
+{
+ if (intparm->pc0)
+ /* ETR port 0 state change. */
+ set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
+ if (intparm->pc1)
+ /* ETR port 1 state change. */
+ set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);
+ if (intparm->eai)
+ /*
+ * ETR port alert on either port 0, 1 or both.
+ * Both ports are not up-to-date now.
+ */
+ set_bit(ETR_EVENT_PORT_ALERT, &etr_events);
+ queue_work(time_sync_wq, &etr_work);
+}
+
+static void etr_timeout(unsigned long dummy)
+{
+ set_bit(ETR_EVENT_UPDATE, &etr_events);
+ queue_work(time_sync_wq, &etr_work);
+}
+
+/*
+ * Check if the etr mode is pss.
+ */
+static inline int etr_mode_is_pps(struct etr_eacr eacr)
+{
+ return eacr.es && !eacr.sl;
+}
+
+/*
+ * Check if the etr mode is etr.
+ */
+static inline int etr_mode_is_etr(struct etr_eacr eacr)
+{
+ return eacr.es && eacr.sl;
+}
+
+/*
+ * Check if the port can be used for TOD synchronization.
+ * For PPS mode the port has to receive OTEs. For ETR mode
+ * the port has to receive OTEs, the ETR stepping bit has to
+ * be zero and the validity bits for data frame 1, 2, and 3
+ * have to be 1.
+ */
+static int etr_port_valid(struct etr_aib *aib, int port)
+{
+ unsigned int psc;
+
+ /* Check that this port is receiving OTEs. */
+ if (aib->tsp == 0)
+ return 0;
+
+ psc = port ? aib->esw.psc1 : aib->esw.psc0;
+ if (psc == etr_lpsc_pps_mode)
+ return 1;
+ if (psc == etr_lpsc_operational_step)
+ return !aib->esw.y && aib->slsw.v1 &&
+ aib->slsw.v2 && aib->slsw.v3;
+ return 0;
+}
+
+/*
+ * Check if two ports are on the same network.
+ */
+static int etr_compare_network(struct etr_aib *aib1, struct etr_aib *aib2)
+{
+ // FIXME: any other fields we have to compare?
+ return aib1->edf1.net_id == aib2->edf1.net_id;
+}
+
+/*
+ * Wrapper for etr_stei that converts physical port states
+ * to logical port states to be consistent with the output
+ * of stetr (see etr_psc vs. etr_lpsc).
+ */
+static void etr_steai_cv(struct etr_aib *aib, unsigned int func)
+{
+ BUG_ON(etr_steai(aib, func) != 0);
+ /* Convert port state to logical port state. */
+ if (aib->esw.psc0 == 1)
+ aib->esw.psc0 = 2;
+ else if (aib->esw.psc0 == 0 && aib->esw.p == 0)
+ aib->esw.psc0 = 1;
+ if (aib->esw.psc1 == 1)
+ aib->esw.psc1 = 2;
+ else if (aib->esw.psc1 == 0 && aib->esw.p == 1)
+ aib->esw.psc1 = 1;
+}
+
+/*
+ * Check if the aib a2 is still connected to the same attachment as
+ * aib a1, the etv values differ by one and a2 is valid.
+ */
+static int etr_aib_follows(struct etr_aib *a1, struct etr_aib *a2, int p)
+{
+ int state_a1, state_a2;
+
+ /* Paranoia check: e0/e1 should better be the same. */
+ if (a1->esw.eacr.e0 != a2->esw.eacr.e0 ||
+ a1->esw.eacr.e1 != a2->esw.eacr.e1)
+ return 0;
+
+ /* Still connected to the same etr ? */
+ state_a1 = p ? a1->esw.psc1 : a1->esw.psc0;
+ state_a2 = p ? a2->esw.psc1 : a2->esw.psc0;
+ if (state_a1 == etr_lpsc_operational_step) {
+ if (state_a2 != etr_lpsc_operational_step ||
+ a1->edf1.net_id != a2->edf1.net_id ||
+ a1->edf1.etr_id != a2->edf1.etr_id ||
+ a1->edf1.etr_pn != a2->edf1.etr_pn)
+ return 0;
+ } else if (state_a2 != etr_lpsc_pps_mode)
+ return 0;
+
+ /* The ETV value of a2 needs to be ETV of a1 + 1. */
+ if (a1->edf2.etv + 1 != a2->edf2.etv)
+ return 0;
+
+ if (!etr_port_valid(a2, p))
+ return 0;
+
+ return 1;
+}
+
+struct clock_sync_data {
+ atomic_t cpus;
+ int in_sync;
+ unsigned long long fixup_cc;
+ int etr_port;
+ struct etr_aib *etr_aib;
+};
+
+static void clock_sync_cpu(struct clock_sync_data *sync)
+{
+ atomic_dec(&sync->cpus);
+ enable_sync_clock();
+ /*
+ * This looks like a busy wait loop but it isn't. etr_sync_cpus
+ * is called on all other cpus while the TOD clocks is stopped.
+ * __udelay will stop the cpu on an enabled wait psw until the
+ * TOD is running again.
+ */
+ while (sync->in_sync == 0) {
+ __udelay(1);
+ /*
+ * A different cpu changes *in_sync. Therefore use
+ * barrier() to force memory access.
+ */
+ barrier();
+ }
+ if (sync->in_sync != 1)
+ /* Didn't work. Clear per-cpu in sync bit again. */
+ disable_sync_clock(NULL);
+ /*
+ * This round of TOD syncing is done. Set the clock comparator
+ * to the next tick and let the processor continue.
+ */
+ fixup_clock_comparator(sync->fixup_cc);
+}
+
+/*
+ * Sync the TOD clock using the port referred to by aibp. This port
+ * has to be enabled and the other port has to be disabled. The
+ * last eacr update has to be more than 1.6 seconds in the past.
+ */
+static int etr_sync_clock(void *data)
+{
+ static int first;
+ unsigned long long clock, old_clock, delay, delta;
+ struct clock_sync_data *etr_sync;
+ struct etr_aib *sync_port, *aib;
+ int port;
+ int rc;
+
+ etr_sync = data;
+
+ if (xchg(&first, 1) == 1) {
+ /* Slave */
+ clock_sync_cpu(etr_sync);
+ return 0;
+ }
+
+ /* Wait until all other cpus entered the sync function. */
+ while (atomic_read(&etr_sync->cpus) != 0)
+ cpu_relax();
+
+ port = etr_sync->etr_port;
+ aib = etr_sync->etr_aib;
+ sync_port = (port == 0) ? &etr_port0 : &etr_port1;
+ enable_sync_clock();
+
+ /* Set clock to next OTE. */
+ __ctl_set_bit(14, 21);
+ __ctl_set_bit(0, 29);
+ clock = ((unsigned long long) (aib->edf2.etv + 1)) << 32;
+ old_clock = get_clock();
+ if (set_clock(clock) == 0) {
+ __udelay(1); /* Wait for the clock to start. */
+ __ctl_clear_bit(0, 29);
+ __ctl_clear_bit(14, 21);
+ etr_stetr(aib);
+ /* Adjust Linux timing variables. */
+ delay = (unsigned long long)
+ (aib->edf2.etv - sync_port->edf2.etv) << 32;
+ delta = adjust_time(old_clock, clock, delay);
+ etr_sync->fixup_cc = delta;
+ fixup_clock_comparator(delta);
+ /* Verify that the clock is properly set. */
+ if (!etr_aib_follows(sync_port, aib, port)) {
+ /* Didn't work. */
+ disable_sync_clock(NULL);
+ etr_sync->in_sync = -EAGAIN;
+ rc = -EAGAIN;
+ } else {
+ etr_sync->in_sync = 1;
+ rc = 0;
+ }
+ } else {
+ /* Could not set the clock ?!? */
+ __ctl_clear_bit(0, 29);
+ __ctl_clear_bit(14, 21);
+ disable_sync_clock(NULL);
+ etr_sync->in_sync = -EAGAIN;
+ rc = -EAGAIN;
+ }
+ xchg(&first, 0);
+ return rc;
+}
+
+static int etr_sync_clock_stop(struct etr_aib *aib, int port)
+{
+ struct clock_sync_data etr_sync;
+ struct etr_aib *sync_port;
+ int follows;
+ int rc;
+
+ /* Check if the current aib is adjacent to the sync port aib. */
+ sync_port = (port == 0) ? &etr_port0 : &etr_port1;
+ follows = etr_aib_follows(sync_port, aib, port);
+ memcpy(sync_port, aib, sizeof(*aib));
+ if (!follows)
+ return -EAGAIN;
+ memset(&etr_sync, 0, sizeof(etr_sync));
+ etr_sync.etr_aib = aib;
+ etr_sync.etr_port = port;
+ get_online_cpus();
+ atomic_set(&etr_sync.cpus, num_online_cpus() - 1);
+ rc = stop_machine(etr_sync_clock, &etr_sync, cpu_online_mask);
+ put_online_cpus();
+ return rc;
+}
+
+/*
+ * Handle the immediate effects of the different events.
+ * The port change event is used for online/offline changes.
+ */
+static struct etr_eacr etr_handle_events(struct etr_eacr eacr)
+{
+ if (test_and_clear_bit(ETR_EVENT_SYNC_CHECK, &etr_events))
+ eacr.es = 0;
+ if (test_and_clear_bit(ETR_EVENT_SWITCH_LOCAL, &etr_events))
+ eacr.es = eacr.sl = 0;
+ if (test_and_clear_bit(ETR_EVENT_PORT_ALERT, &etr_events))
+ etr_port0_uptodate = etr_port1_uptodate = 0;
+
+ if (test_and_clear_bit(ETR_EVENT_PORT0_CHANGE, &etr_events)) {
+ if (eacr.e0)
+ /*
+ * Port change of an enabled port. We have to
+ * assume that this can have caused an stepping
+ * port switch.
+ */
+ etr_tolec = get_clock();
+ eacr.p0 = etr_port0_online;
+ if (!eacr.p0)
+ eacr.e0 = 0;
+ etr_port0_uptodate = 0;
+ }
+ if (test_and_clear_bit(ETR_EVENT_PORT1_CHANGE, &etr_events)) {
+ if (eacr.e1)
+ /*
+ * Port change of an enabled port. We have to
+ * assume that this can have caused an stepping
+ * port switch.
+ */
+ etr_tolec = get_clock();
+ eacr.p1 = etr_port1_online;
+ if (!eacr.p1)
+ eacr.e1 = 0;
+ etr_port1_uptodate = 0;
+ }
+ clear_bit(ETR_EVENT_UPDATE, &etr_events);
+ return eacr;
+}
+
+/*
+ * Set up a timer that expires after the etr_tolec + 1.6 seconds if
+ * one of the ports needs an update.
+ */
+static void etr_set_tolec_timeout(unsigned long long now)
+{
+ unsigned long micros;
+
+ if ((!etr_eacr.p0 || etr_port0_uptodate) &&
+ (!etr_eacr.p1 || etr_port1_uptodate))
+ return;
+ micros = (now > etr_tolec) ? ((now - etr_tolec) >> 12) : 0;
+ micros = (micros > 1600000) ? 0 : 1600000 - micros;
+ mod_timer(&etr_timer, jiffies + (micros * HZ) / 1000000 + 1);
+}
+
+/*
+ * Set up a time that expires after 1/2 second.
+ */
+static void etr_set_sync_timeout(void)
+{
+ mod_timer(&etr_timer, jiffies + HZ/2);
+}
+
+/*
+ * Update the aib information for one or both ports.
+ */
+static struct etr_eacr etr_handle_update(struct etr_aib *aib,
+ struct etr_eacr eacr)
+{
+ /* With both ports disabled the aib information is useless. */
+ if (!eacr.e0 && !eacr.e1)
+ return eacr;
+
+ /* Update port0 or port1 with aib stored in etr_work_fn. */
+ if (aib->esw.q == 0) {
+ /* Information for port 0 stored. */
+ if (eacr.p0 && !etr_port0_uptodate) {
+ etr_port0 = *aib;
+ if (etr_port0_online)
+ etr_port0_uptodate = 1;
+ }
+ } else {
+ /* Information for port 1 stored. */
+ if (eacr.p1 && !etr_port1_uptodate) {
+ etr_port1 = *aib;
+ if (etr_port0_online)
+ etr_port1_uptodate = 1;
+ }
+ }
+
+ /*
+ * Do not try to get the alternate port aib if the clock
+ * is not in sync yet.
+ */
+ if (!eacr.es || !check_sync_clock())
+ return eacr;
+
+ /*
+ * If steai is available we can get the information about
+ * the other port immediately. If only stetr is available the
+ * data-port bit toggle has to be used.
+ */
+ if (etr_steai_available) {
+ if (eacr.p0 && !etr_port0_uptodate) {
+ etr_steai_cv(&etr_port0, ETR_STEAI_PORT_0);
+ etr_port0_uptodate = 1;
+ }
+ if (eacr.p1 && !etr_port1_uptodate) {
+ etr_steai_cv(&etr_port1, ETR_STEAI_PORT_1);
+ etr_port1_uptodate = 1;
+ }
+ } else {
+ /*
+ * One port was updated above, if the other
+ * port is not uptodate toggle dp bit.
+ */
+ if ((eacr.p0 && !etr_port0_uptodate) ||
+ (eacr.p1 && !etr_port1_uptodate))
+ eacr.dp ^= 1;
+ else
+ eacr.dp = 0;
+ }
+ return eacr;
+}
+
+/*
+ * Write new etr control register if it differs from the current one.
+ * Return 1 if etr_tolec has been updated as well.
+ */
+static void etr_update_eacr(struct etr_eacr eacr)
+{
+ int dp_changed;
+
+ if (memcmp(&etr_eacr, &eacr, sizeof(eacr)) == 0)
+ /* No change, return. */
+ return;
+ /*
+ * The disable of an active port of the change of the data port
+ * bit can/will cause a change in the data port.
+ */
+ dp_changed = etr_eacr.e0 > eacr.e0 || etr_eacr.e1 > eacr.e1 ||
+ (etr_eacr.dp ^ eacr.dp) != 0;
+ etr_eacr = eacr;
+ etr_setr(&etr_eacr);
+ if (dp_changed)
+ etr_tolec = get_clock();
+}
+
+/*
+ * ETR work. In this function you'll find the main logic. In
+ * particular this is the only function that calls etr_update_eacr(),
+ * it "controls" the etr control register.
+ */
+static void etr_work_fn(struct work_struct *work)
+{
+ unsigned long long now;
+ struct etr_eacr eacr;
+ struct etr_aib aib;
+ int sync_port;
+
+ /* prevent multiple execution. */
+ mutex_lock(&etr_work_mutex);
+
+ /* Create working copy of etr_eacr. */
+ eacr = etr_eacr;
+
+ /* Check for the different events and their immediate effects. */
+ eacr = etr_handle_events(eacr);
+
+ /* Check if ETR is supposed to be active. */
+ eacr.ea = eacr.p0 || eacr.p1;
+ if (!eacr.ea) {
+ /* Both ports offline. Reset everything. */
+ eacr.dp = eacr.es = eacr.sl = 0;
+ on_each_cpu(disable_sync_clock, NULL, 1);
+ del_timer_sync(&etr_timer);
+ etr_update_eacr(eacr);
+ goto out_unlock;
+ }
+
+ /* Store aib to get the current ETR status word. */
+ BUG_ON(etr_stetr(&aib) != 0);
+ etr_port0.esw = etr_port1.esw = aib.esw; /* Copy status word. */
+ now = get_clock();
+
+ /*
+ * Update the port information if the last stepping port change
+ * or data port change is older than 1.6 seconds.
+ */
+ if (now >= etr_tolec + (1600000 << 12))
+ eacr = etr_handle_update(&aib, eacr);
+
+ /*
+ * Select ports to enable. The preferred synchronization mode is PPS.
+ * If a port can be enabled depends on a number of things:
+ * 1) The port needs to be online and uptodate. A port is not
+ * disabled just because it is not uptodate, but it is only
+ * enabled if it is uptodate.
+ * 2) The port needs to have the same mode (pps / etr).
+ * 3) The port needs to be usable -> etr_port_valid() == 1
+ * 4) To enable the second port the clock needs to be in sync.
+ * 5) If both ports are useable and are ETR ports, the network id
+ * has to be the same.
+ * The eacr.sl bit is used to indicate etr mode vs. pps mode.
+ */
+ if (eacr.p0 && aib.esw.psc0 == etr_lpsc_pps_mode) {
+ eacr.sl = 0;
+ eacr.e0 = 1;
+ if (!etr_mode_is_pps(etr_eacr))
+ eacr.es = 0;
+ if (!eacr.es || !eacr.p1 || aib.esw.psc1 != etr_lpsc_pps_mode)
+ eacr.e1 = 0;
+ // FIXME: uptodate checks ?
+ else if (etr_port0_uptodate && etr_port1_uptodate)
+ eacr.e1 = 1;
+ sync_port = (etr_port0_uptodate &&
+ etr_port_valid(&etr_port0, 0)) ? 0 : -1;
+ } else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_pps_mode) {
+ eacr.sl = 0;
+ eacr.e0 = 0;
+ eacr.e1 = 1;
+ if (!etr_mode_is_pps(etr_eacr))
+ eacr.es = 0;
+ sync_port = (etr_port1_uptodate &&
+ etr_port_valid(&etr_port1, 1)) ? 1 : -1;
+ } else if (eacr.p0 && aib.esw.psc0 == etr_lpsc_operational_step) {
+ eacr.sl = 1;
+ eacr.e0 = 1;
+ if (!etr_mode_is_etr(etr_eacr))
+ eacr.es = 0;
+ if (!eacr.es || !eacr.p1 ||
+ aib.esw.psc1 != etr_lpsc_operational_alt)
+ eacr.e1 = 0;
+ else if (etr_port0_uptodate && etr_port1_uptodate &&
+ etr_compare_network(&etr_port0, &etr_port1))
+ eacr.e1 = 1;
+ sync_port = (etr_port0_uptodate &&
+ etr_port_valid(&etr_port0, 0)) ? 0 : -1;
+ } else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_operational_step) {
+ eacr.sl = 1;
+ eacr.e0 = 0;
+ eacr.e1 = 1;
+ if (!etr_mode_is_etr(etr_eacr))
+ eacr.es = 0;
+ sync_port = (etr_port1_uptodate &&
+ etr_port_valid(&etr_port1, 1)) ? 1 : -1;
+ } else {
+ /* Both ports not usable. */
+ eacr.es = eacr.sl = 0;
+ sync_port = -1;
+ }
+
+ /*
+ * If the clock is in sync just update the eacr and return.
+ * If there is no valid sync port wait for a port update.
+ */
+ if ((eacr.es && check_sync_clock()) || sync_port < 0) {
+ etr_update_eacr(eacr);
+ etr_set_tolec_timeout(now);
+ goto out_unlock;
+ }
+
+ /*
+ * Prepare control register for clock syncing
+ * (reset data port bit, set sync check control.
+ */
+ eacr.dp = 0;
+ eacr.es = 1;
+
+ /*
+ * Update eacr and try to synchronize the clock. If the update
+ * of eacr caused a stepping port switch (or if we have to
+ * assume that a stepping port switch has occurred) or the
+ * clock syncing failed, reset the sync check control bit
+ * and set up a timer to try again after 0.5 seconds
+ */
+ etr_update_eacr(eacr);
+ if (now < etr_tolec + (1600000 << 12) ||
+ etr_sync_clock_stop(&aib, sync_port) != 0) {
+ /* Sync failed. Try again in 1/2 second. */
+ eacr.es = 0;
+ etr_update_eacr(eacr);
+ etr_set_sync_timeout();
+ } else
+ etr_set_tolec_timeout(now);
+out_unlock:
+ mutex_unlock(&etr_work_mutex);
+}
+
+/*
+ * Sysfs interface functions
+ */
+static struct bus_type etr_subsys = {
+ .name = "etr",
+ .dev_name = "etr",
+};
+
+static struct device etr_port0_dev = {
+ .id = 0,
+ .bus = &etr_subsys,
+};
+
+static struct device etr_port1_dev = {
+ .id = 1,
+ .bus = &etr_subsys,
+};
+
+/*
+ * ETR subsys attributes
+ */
+static ssize_t etr_stepping_port_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%i\n", etr_port0.esw.p);
+}
+
+static DEVICE_ATTR(stepping_port, 0400, etr_stepping_port_show, NULL);
+
+static ssize_t etr_stepping_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ char *mode_str;
+
+ if (etr_mode_is_pps(etr_eacr))
+ mode_str = "pps";
+ else if (etr_mode_is_etr(etr_eacr))
+ mode_str = "etr";
+ else
+ mode_str = "local";
+ return sprintf(buf, "%s\n", mode_str);
+}
+
+static DEVICE_ATTR(stepping_mode, 0400, etr_stepping_mode_show, NULL);
+
+/*
+ * ETR port attributes
+ */
+static inline struct etr_aib *etr_aib_from_dev(struct device *dev)
+{
+ if (dev == &etr_port0_dev)
+ return etr_port0_online ? &etr_port0 : NULL;
+ else
+ return etr_port1_online ? &etr_port1 : NULL;
+}
+
+static ssize_t etr_online_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ unsigned int online;
+
+ online = (dev == &etr_port0_dev) ? etr_port0_online : etr_port1_online;
+ return sprintf(buf, "%i\n", online);
+}
+
+static ssize_t etr_online_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int value;
+
+ value = simple_strtoul(buf, NULL, 0);
+ if (value != 0 && value != 1)
+ return -EINVAL;
+ if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags))
+ return -EOPNOTSUPP;
+ mutex_lock(&clock_sync_mutex);
+ if (dev == &etr_port0_dev) {
+ if (etr_port0_online == value)
+ goto out; /* Nothing to do. */
+ etr_port0_online = value;
+ if (etr_port0_online && etr_port1_online)
+ set_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
+ else
+ clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
+ set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);
+ queue_work(time_sync_wq, &etr_work);
+ } else {
+ if (etr_port1_online == value)
+ goto out; /* Nothing to do. */
+ etr_port1_online = value;
+ if (etr_port0_online && etr_port1_online)
+ set_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
+ else
+ clear_bit(CLOCK_SYNC_ETR, &clock_sync_flags);
+ set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);
+ queue_work(time_sync_wq, &etr_work);
+ }
+out:
+ mutex_unlock(&clock_sync_mutex);
+ return count;
+}
+
+static DEVICE_ATTR(online, 0600, etr_online_show, etr_online_store);
+
+static ssize_t etr_stepping_control_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%i\n", (dev == &etr_port0_dev) ?
+ etr_eacr.e0 : etr_eacr.e1);
+}
+
+static DEVICE_ATTR(stepping_control, 0400, etr_stepping_control_show, NULL);
+
+static ssize_t etr_mode_code_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!etr_port0_online && !etr_port1_online)
+ /* Status word is not uptodate if both ports are offline. */
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (dev == &etr_port0_dev) ?
+ etr_port0.esw.psc0 : etr_port0.esw.psc1);
+}
+
+static DEVICE_ATTR(state_code, 0400, etr_mode_code_show, NULL);
+
+static ssize_t etr_untuned_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct etr_aib *aib = etr_aib_from_dev(dev);
+
+ if (!aib || !aib->slsw.v1)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", aib->edf1.u);
+}
+
+static DEVICE_ATTR(untuned, 0400, etr_untuned_show, NULL);
+
+static ssize_t etr_network_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct etr_aib *aib = etr_aib_from_dev(dev);
+
+ if (!aib || !aib->slsw.v1)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", aib->edf1.net_id);
+}
+
+static DEVICE_ATTR(network, 0400, etr_network_id_show, NULL);
+
+static ssize_t etr_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct etr_aib *aib = etr_aib_from_dev(dev);
+
+ if (!aib || !aib->slsw.v1)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", aib->edf1.etr_id);
+}
+
+static DEVICE_ATTR(id, 0400, etr_id_show, NULL);
+
+static ssize_t etr_port_number_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct etr_aib *aib = etr_aib_from_dev(dev);
+
+ if (!aib || !aib->slsw.v1)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", aib->edf1.etr_pn);
+}
+
+static DEVICE_ATTR(port, 0400, etr_port_number_show, NULL);
+
+static ssize_t etr_coupled_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct etr_aib *aib = etr_aib_from_dev(dev);
+
+ if (!aib || !aib->slsw.v3)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", aib->edf3.c);
+}
+
+static DEVICE_ATTR(coupled, 0400, etr_coupled_show, NULL);
+
+static ssize_t etr_local_time_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct etr_aib *aib = etr_aib_from_dev(dev);
+
+ if (!aib || !aib->slsw.v3)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", aib->edf3.blto);
+}
+
+static DEVICE_ATTR(local_time, 0400, etr_local_time_show, NULL);
+
+static ssize_t etr_utc_offset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct etr_aib *aib = etr_aib_from_dev(dev);
+
+ if (!aib || !aib->slsw.v3)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", aib->edf3.buo);
+}
+
+static DEVICE_ATTR(utc_offset, 0400, etr_utc_offset_show, NULL);
+
+static struct device_attribute *etr_port_attributes[] = {
+ &dev_attr_online,
+ &dev_attr_stepping_control,
+ &dev_attr_state_code,
+ &dev_attr_untuned,
+ &dev_attr_network,
+ &dev_attr_id,
+ &dev_attr_port,
+ &dev_attr_coupled,
+ &dev_attr_local_time,
+ &dev_attr_utc_offset,
+ NULL
+};
+
+static int __init etr_register_port(struct device *dev)
+{
+ struct device_attribute **attr;
+ int rc;
+
+ rc = device_register(dev);
+ if (rc)
+ goto out;
+ for (attr = etr_port_attributes; *attr; attr++) {
+ rc = device_create_file(dev, *attr);
+ if (rc)
+ goto out_unreg;
+ }
+ return 0;
+out_unreg:
+ for (; attr >= etr_port_attributes; attr--)
+ device_remove_file(dev, *attr);
+ device_unregister(dev);
+out:
+ return rc;
+}
+
+static void __init etr_unregister_port(struct device *dev)
+{
+ struct device_attribute **attr;
+
+ for (attr = etr_port_attributes; *attr; attr++)
+ device_remove_file(dev, *attr);
+ device_unregister(dev);
+}
+
+static int __init etr_init_sysfs(void)
+{
+ int rc;
+
+ rc = subsys_system_register(&etr_subsys, NULL);
+ if (rc)
+ goto out;
+ rc = device_create_file(etr_subsys.dev_root, &dev_attr_stepping_port);
+ if (rc)
+ goto out_unreg_subsys;
+ rc = device_create_file(etr_subsys.dev_root, &dev_attr_stepping_mode);
+ if (rc)
+ goto out_remove_stepping_port;
+ rc = etr_register_port(&etr_port0_dev);
+ if (rc)
+ goto out_remove_stepping_mode;
+ rc = etr_register_port(&etr_port1_dev);
+ if (rc)
+ goto out_remove_port0;
+ return 0;
+
+out_remove_port0:
+ etr_unregister_port(&etr_port0_dev);
+out_remove_stepping_mode:
+ device_remove_file(etr_subsys.dev_root, &dev_attr_stepping_mode);
+out_remove_stepping_port:
+ device_remove_file(etr_subsys.dev_root, &dev_attr_stepping_port);
+out_unreg_subsys:
+ bus_unregister(&etr_subsys);
+out:
+ return rc;
+}
+
+device_initcall(etr_init_sysfs);
+
+/*
+ * Server Time Protocol (STP) code.
+ */
+static int stp_online;
+static struct stp_sstpi stp_info;
+static void *stp_page;
+
+static void stp_work_fn(struct work_struct *work);
+static DEFINE_MUTEX(stp_work_mutex);
+static DECLARE_WORK(stp_work, stp_work_fn);
+static struct timer_list stp_timer;
+
+static int __init early_parse_stp(char *p)
+{
+ if (strncmp(p, "off", 3) == 0)
+ stp_online = 0;
+ else if (strncmp(p, "on", 2) == 0)
+ stp_online = 1;
+ return 0;
+}
+early_param("stp", early_parse_stp);
+
+/*
+ * Reset STP attachment.
+ */
+static void __init stp_reset(void)
+{
+ int rc;
+
+ stp_page = (void *) get_zeroed_page(GFP_ATOMIC);
+ rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
+ if (rc == 0)
+ set_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags);
+ else if (stp_online) {
+ pr_warning("The real or virtual hardware system does "
+ "not provide an STP interface\n");
+ free_page((unsigned long) stp_page);
+ stp_page = NULL;
+ stp_online = 0;
+ }
+}
+
+static void stp_timeout(unsigned long dummy)
+{
+ queue_work(time_sync_wq, &stp_work);
+}
+
+static int __init stp_init(void)
+{
+ if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
+ return 0;
+ setup_timer(&stp_timer, stp_timeout, 0UL);
+ time_init_wq();
+ if (!stp_online)
+ return 0;
+ queue_work(time_sync_wq, &stp_work);
+ return 0;
+}
+
+arch_initcall(stp_init);
+
+/*
+ * STP timing alert. There are three causes:
+ * 1) timing status change
+ * 2) link availability change
+ * 3) time control parameter change
+ * In all three cases we are only interested in the clock source state.
+ * If a STP clock source is now available use it.
+ */
+static void stp_timing_alert(struct stp_irq_parm *intparm)
+{
+ if (intparm->tsc || intparm->lac || intparm->tcpc)
+ queue_work(time_sync_wq, &stp_work);
+}
+
+/*
+ * STP sync check machine check. This is called when the timing state
+ * changes from the synchronized state to the unsynchronized state.
+ * After a STP sync check the clock is not in sync. The machine check
+ * is broadcasted to all cpus at the same time.
+ */
+void stp_sync_check(void)
+{
+ disable_sync_clock(NULL);
+ queue_work(time_sync_wq, &stp_work);
+}
+
+/*
+ * STP island condition machine check. This is called when an attached
+ * server attempts to communicate over an STP link and the servers
+ * have matching CTN ids and have a valid stratum-1 configuration
+ * but the configurations do not match.
+ */
+void stp_island_check(void)
+{
+ disable_sync_clock(NULL);
+ queue_work(time_sync_wq, &stp_work);
+}
+
+
+static int stp_sync_clock(void *data)
+{
+ static int first;
+ unsigned long long old_clock, delta;
+ struct clock_sync_data *stp_sync;
+ int rc;
+
+ stp_sync = data;
+
+ if (xchg(&first, 1) == 1) {
+ /* Slave */
+ clock_sync_cpu(stp_sync);
+ return 0;
+ }
+
+ /* Wait until all other cpus entered the sync function. */
+ while (atomic_read(&stp_sync->cpus) != 0)
+ cpu_relax();
+
+ enable_sync_clock();
+
+ rc = 0;
+ if (stp_info.todoff[0] || stp_info.todoff[1] ||
+ stp_info.todoff[2] || stp_info.todoff[3] ||
+ stp_info.tmd != 2) {
+ old_clock = get_clock();
+ rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0);
+ if (rc == 0) {
+ delta = adjust_time(old_clock, get_clock(), 0);
+ fixup_clock_comparator(delta);
+ rc = chsc_sstpi(stp_page, &stp_info,
+ sizeof(struct stp_sstpi));
+ if (rc == 0 && stp_info.tmd != 2)
+ rc = -EAGAIN;
+ }
+ }
+ if (rc) {
+ disable_sync_clock(NULL);
+ stp_sync->in_sync = -EAGAIN;
+ } else
+ stp_sync->in_sync = 1;
+ xchg(&first, 0);
+ return 0;
+}
+
+/*
+ * STP work. Check for the STP state and take over the clock
+ * synchronization if the STP clock source is usable.
+ */
+static void stp_work_fn(struct work_struct *work)
+{
+ struct clock_sync_data stp_sync;
+ int rc;
+
+ /* prevent multiple execution. */
+ mutex_lock(&stp_work_mutex);
+
+ if (!stp_online) {
+ chsc_sstpc(stp_page, STP_OP_CTRL, 0x0000);
+ del_timer_sync(&stp_timer);
+ goto out_unlock;
+ }
+
+ rc = chsc_sstpc(stp_page, STP_OP_CTRL, 0xb0e0);
+ if (rc)
+ goto out_unlock;
+
+ rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi));
+ if (rc || stp_info.c == 0)
+ goto out_unlock;
+
+ /* Skip synchronization if the clock is already in sync. */
+ if (check_sync_clock())
+ goto out_unlock;
+
+ memset(&stp_sync, 0, sizeof(stp_sync));
+ get_online_cpus();
+ atomic_set(&stp_sync.cpus, num_online_cpus() - 1);
+ stop_machine(stp_sync_clock, &stp_sync, cpu_online_mask);
+ put_online_cpus();
+
+ if (!check_sync_clock())
+ /*
+ * There is a usable clock but the synchonization failed.
+ * Retry after a second.
+ */
+ mod_timer(&stp_timer, jiffies + HZ);
+
+out_unlock:
+ mutex_unlock(&stp_work_mutex);
+}
+
+/*
+ * STP subsys sysfs interface functions
+ */
+static struct bus_type stp_subsys = {
+ .name = "stp",
+ .dev_name = "stp",
+};
+
+static ssize_t stp_ctn_id_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ if (!stp_online)
+ return -ENODATA;
+ return sprintf(buf, "%016llx\n",
+ *(unsigned long long *) stp_info.ctnid);
+}
+
+static DEVICE_ATTR(ctn_id, 0400, stp_ctn_id_show, NULL);
+
+static ssize_t stp_ctn_type_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ if (!stp_online)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", stp_info.ctn);
+}
+
+static DEVICE_ATTR(ctn_type, 0400, stp_ctn_type_show, NULL);
+
+static ssize_t stp_dst_offset_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ if (!stp_online || !(stp_info.vbits & 0x2000))
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (int)(s16) stp_info.dsto);
+}
+
+static DEVICE_ATTR(dst_offset, 0400, stp_dst_offset_show, NULL);
+
+static ssize_t stp_leap_seconds_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ if (!stp_online || !(stp_info.vbits & 0x8000))
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (int)(s16) stp_info.leaps);
+}
+
+static DEVICE_ATTR(leap_seconds, 0400, stp_leap_seconds_show, NULL);
+
+static ssize_t stp_stratum_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ if (!stp_online)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (int)(s16) stp_info.stratum);
+}
+
+static DEVICE_ATTR(stratum, 0400, stp_stratum_show, NULL);
+
+static ssize_t stp_time_offset_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ if (!stp_online || !(stp_info.vbits & 0x0800))
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (int) stp_info.tto);
+}
+
+static DEVICE_ATTR(time_offset, 0400, stp_time_offset_show, NULL);
+
+static ssize_t stp_time_zone_offset_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ if (!stp_online || !(stp_info.vbits & 0x4000))
+ return -ENODATA;
+ return sprintf(buf, "%i\n", (int)(s16) stp_info.tzo);
+}
+
+static DEVICE_ATTR(time_zone_offset, 0400,
+ stp_time_zone_offset_show, NULL);
+
+static ssize_t stp_timing_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ if (!stp_online)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", stp_info.tmd);
+}
+
+static DEVICE_ATTR(timing_mode, 0400, stp_timing_mode_show, NULL);
+
+static ssize_t stp_timing_state_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ if (!stp_online)
+ return -ENODATA;
+ return sprintf(buf, "%i\n", stp_info.tst);
+}
+
+static DEVICE_ATTR(timing_state, 0400, stp_timing_state_show, NULL);
+
+static ssize_t stp_online_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%i\n", stp_online);
+}
+
+static ssize_t stp_online_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int value;
+
+ value = simple_strtoul(buf, NULL, 0);
+ if (value != 0 && value != 1)
+ return -EINVAL;
+ if (!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
+ return -EOPNOTSUPP;
+ mutex_lock(&clock_sync_mutex);
+ stp_online = value;
+ if (stp_online)
+ set_bit(CLOCK_SYNC_STP, &clock_sync_flags);
+ else
+ clear_bit(CLOCK_SYNC_STP, &clock_sync_flags);
+ queue_work(time_sync_wq, &stp_work);
+ mutex_unlock(&clock_sync_mutex);
+ return count;
+}
+
+/*
+ * Can't use DEVICE_ATTR because the attribute should be named
+ * stp/online but dev_attr_online already exists in this file ..
+ */
+static struct device_attribute dev_attr_stp_online = {
+ .attr = { .name = "online", .mode = 0600 },
+ .show = stp_online_show,
+ .store = stp_online_store,
+};
+
+static struct device_attribute *stp_attributes[] = {
+ &dev_attr_ctn_id,
+ &dev_attr_ctn_type,
+ &dev_attr_dst_offset,
+ &dev_attr_leap_seconds,
+ &dev_attr_stp_online,
+ &dev_attr_stratum,
+ &dev_attr_time_offset,
+ &dev_attr_time_zone_offset,
+ &dev_attr_timing_mode,
+ &dev_attr_timing_state,
+ NULL
+};
+
+static int __init stp_init_sysfs(void)
+{
+ struct device_attribute **attr;
+ int rc;
+
+ rc = subsys_system_register(&stp_subsys, NULL);
+ if (rc)
+ goto out;
+ for (attr = stp_attributes; *attr; attr++) {
+ rc = device_create_file(stp_subsys.dev_root, *attr);
+ if (rc)
+ goto out_unreg;
+ }
+ return 0;
+out_unreg:
+ for (; attr >= stp_attributes; attr--)
+ device_remove_file(stp_subsys.dev_root, *attr);
+ bus_unregister(&stp_subsys);
+out:
+ return rc;
+}
+
+device_initcall(stp_init_sysfs);
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
new file mode 100644
index 00000000..4f8dc942
--- /dev/null
+++ b/arch/s390/kernel/topology.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright IBM Corp. 2007,2011
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#define KMSG_COMPONENT "cpu"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/workqueue.h>
+#include <linux/bootmem.h>
+#include <linux/cpuset.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/mm.h>
+
+#define PTF_HORIZONTAL (0UL)
+#define PTF_VERTICAL (1UL)
+#define PTF_CHECK (2UL)
+
+struct mask_info {
+ struct mask_info *next;
+ unsigned char id;
+ cpumask_t mask;
+};
+
+static int topology_enabled = 1;
+static void topology_work_fn(struct work_struct *work);
+static struct sysinfo_15_1_x *tl_info;
+static void set_topology_timer(void);
+static DECLARE_WORK(topology_work, topology_work_fn);
+/* topology_lock protects the core linked list */
+static DEFINE_SPINLOCK(topology_lock);
+
+static struct mask_info core_info;
+cpumask_t cpu_core_map[NR_CPUS];
+unsigned char cpu_core_id[NR_CPUS];
+
+static struct mask_info book_info;
+cpumask_t cpu_book_map[NR_CPUS];
+unsigned char cpu_book_id[NR_CPUS];
+
+/* smp_cpu_state_mutex must be held when accessing this array */
+int cpu_polarization[NR_CPUS];
+
+static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu)
+{
+ cpumask_t mask;
+
+ cpumask_clear(&mask);
+ if (!topology_enabled || !MACHINE_HAS_TOPOLOGY) {
+ cpumask_copy(&mask, cpumask_of(cpu));
+ return mask;
+ }
+ while (info) {
+ if (cpumask_test_cpu(cpu, &info->mask)) {
+ mask = info->mask;
+ break;
+ }
+ info = info->next;
+ }
+ if (cpumask_empty(&mask))
+ cpumask_copy(&mask, cpumask_of(cpu));
+ return mask;
+}
+
+static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu,
+ struct mask_info *book,
+ struct mask_info *core,
+ int one_core_per_cpu)
+{
+ unsigned int cpu;
+
+ for (cpu = find_first_bit(&tl_cpu->mask[0], TOPOLOGY_CPU_BITS);
+ cpu < TOPOLOGY_CPU_BITS;
+ cpu = find_next_bit(&tl_cpu->mask[0], TOPOLOGY_CPU_BITS, cpu + 1))
+ {
+ unsigned int rcpu;
+ int lcpu;
+
+ rcpu = TOPOLOGY_CPU_BITS - 1 - cpu + tl_cpu->origin;
+ lcpu = smp_find_processor_id(rcpu);
+ if (lcpu >= 0) {
+ cpumask_set_cpu(lcpu, &book->mask);
+ cpu_book_id[lcpu] = book->id;
+ cpumask_set_cpu(lcpu, &core->mask);
+ if (one_core_per_cpu) {
+ cpu_core_id[lcpu] = rcpu;
+ core = core->next;
+ } else {
+ cpu_core_id[lcpu] = core->id;
+ }
+ cpu_set_polarization(lcpu, tl_cpu->pp);
+ }
+ }
+ return core;
+}
+
+static void clear_masks(void)
+{
+ struct mask_info *info;
+
+ info = &core_info;
+ while (info) {
+ cpumask_clear(&info->mask);
+ info = info->next;
+ }
+ info = &book_info;
+ while (info) {
+ cpumask_clear(&info->mask);
+ info = info->next;
+ }
+}
+
+static union topology_entry *next_tle(union topology_entry *tle)
+{
+ if (!tle->nl)
+ return (union topology_entry *)((struct topology_cpu *)tle + 1);
+ return (union topology_entry *)((struct topology_container *)tle + 1);
+}
+
+static void __tl_to_cores_generic(struct sysinfo_15_1_x *info)
+{
+ struct mask_info *core = &core_info;
+ struct mask_info *book = &book_info;
+ union topology_entry *tle, *end;
+
+ tle = info->tle;
+ end = (union topology_entry *)((unsigned long)info + info->length);
+ while (tle < end) {
+ switch (tle->nl) {
+ case 2:
+ book = book->next;
+ book->id = tle->container.id;
+ break;
+ case 1:
+ core = core->next;
+ core->id = tle->container.id;
+ break;
+ case 0:
+ add_cpus_to_mask(&tle->cpu, book, core, 0);
+ break;
+ default:
+ clear_masks();
+ return;
+ }
+ tle = next_tle(tle);
+ }
+}
+
+static void __tl_to_cores_z10(struct sysinfo_15_1_x *info)
+{
+ struct mask_info *core = &core_info;
+ struct mask_info *book = &book_info;
+ union topology_entry *tle, *end;
+
+ tle = info->tle;
+ end = (union topology_entry *)((unsigned long)info + info->length);
+ while (tle < end) {
+ switch (tle->nl) {
+ case 1:
+ book = book->next;
+ book->id = tle->container.id;
+ break;
+ case 0:
+ core = add_cpus_to_mask(&tle->cpu, book, core, 1);
+ break;
+ default:
+ clear_masks();
+ return;
+ }
+ tle = next_tle(tle);
+ }
+}
+
+static void tl_to_cores(struct sysinfo_15_1_x *info)
+{
+ struct cpuid cpu_id;
+
+ get_cpu_id(&cpu_id);
+ spin_lock_irq(&topology_lock);
+ clear_masks();
+ switch (cpu_id.machine) {
+ case 0x2097:
+ case 0x2098:
+ __tl_to_cores_z10(info);
+ break;
+ default:
+ __tl_to_cores_generic(info);
+ }
+ spin_unlock_irq(&topology_lock);
+}
+
+static void topology_update_polarization_simple(void)
+{
+ int cpu;
+
+ mutex_lock(&smp_cpu_state_mutex);
+ for_each_possible_cpu(cpu)
+ cpu_set_polarization(cpu, POLARIZATION_HRZ);
+ mutex_unlock(&smp_cpu_state_mutex);
+}
+
+static int ptf(unsigned long fc)
+{
+ int rc;
+
+ asm volatile(
+ " .insn rre,0xb9a20000,%1,%1\n"
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (rc)
+ : "d" (fc) : "cc");
+ return rc;
+}
+
+int topology_set_cpu_management(int fc)
+{
+ int cpu, rc;
+
+ if (!MACHINE_HAS_TOPOLOGY)
+ return -EOPNOTSUPP;
+ if (fc)
+ rc = ptf(PTF_VERTICAL);
+ else
+ rc = ptf(PTF_HORIZONTAL);
+ if (rc)
+ return -EBUSY;
+ for_each_possible_cpu(cpu)
+ cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
+ return rc;
+}
+
+static void update_cpu_core_map(void)
+{
+ unsigned long flags;
+ int cpu;
+
+ spin_lock_irqsave(&topology_lock, flags);
+ for_each_possible_cpu(cpu) {
+ cpu_core_map[cpu] = cpu_group_map(&core_info, cpu);
+ cpu_book_map[cpu] = cpu_group_map(&book_info, cpu);
+ }
+ spin_unlock_irqrestore(&topology_lock, flags);
+}
+
+void store_topology(struct sysinfo_15_1_x *info)
+{
+ int rc;
+
+ rc = stsi(info, 15, 1, 3);
+ if (rc != -ENOSYS)
+ return;
+ stsi(info, 15, 1, 2);
+}
+
+int arch_update_cpu_topology(void)
+{
+ struct sysinfo_15_1_x *info = tl_info;
+ struct device *dev;
+ int cpu;
+
+ if (!MACHINE_HAS_TOPOLOGY) {
+ update_cpu_core_map();
+ topology_update_polarization_simple();
+ return 0;
+ }
+ store_topology(info);
+ tl_to_cores(info);
+ update_cpu_core_map();
+ for_each_online_cpu(cpu) {
+ dev = get_cpu_device(cpu);
+ kobject_uevent(&dev->kobj, KOBJ_CHANGE);
+ }
+ return 1;
+}
+
+static void topology_work_fn(struct work_struct *work)
+{
+ rebuild_sched_domains();
+}
+
+void topology_schedule_update(void)
+{
+ schedule_work(&topology_work);
+}
+
+static void topology_timer_fn(unsigned long ignored)
+{
+ if (ptf(PTF_CHECK))
+ topology_schedule_update();
+ set_topology_timer();
+}
+
+static struct timer_list topology_timer =
+ TIMER_DEFERRED_INITIALIZER(topology_timer_fn, 0, 0);
+
+static atomic_t topology_poll = ATOMIC_INIT(0);
+
+static void set_topology_timer(void)
+{
+ if (atomic_add_unless(&topology_poll, -1, 0))
+ mod_timer(&topology_timer, jiffies + HZ / 10);
+ else
+ mod_timer(&topology_timer, jiffies + HZ * 60);
+}
+
+void topology_expect_change(void)
+{
+ if (!MACHINE_HAS_TOPOLOGY)
+ return;
+ /* This is racy, but it doesn't matter since it is just a heuristic.
+ * Worst case is that we poll in a higher frequency for a bit longer.
+ */
+ if (atomic_read(&topology_poll) > 60)
+ return;
+ atomic_add(60, &topology_poll);
+ set_topology_timer();
+}
+
+static int __init early_parse_topology(char *p)
+{
+ if (strncmp(p, "off", 3))
+ return 0;
+ topology_enabled = 0;
+ return 0;
+}
+early_param("topology", early_parse_topology);
+
+static void __init alloc_masks(struct sysinfo_15_1_x *info,
+ struct mask_info *mask, int offset)
+{
+ int i, nr_masks;
+
+ nr_masks = info->mag[TOPOLOGY_NR_MAG - offset];
+ for (i = 0; i < info->mnest - offset; i++)
+ nr_masks *= info->mag[TOPOLOGY_NR_MAG - offset - 1 - i];
+ nr_masks = max(nr_masks, 1);
+ for (i = 0; i < nr_masks; i++) {
+ mask->next = alloc_bootmem(sizeof(struct mask_info));
+ mask = mask->next;
+ }
+}
+
+void __init s390_init_cpu_topology(void)
+{
+ struct sysinfo_15_1_x *info;
+ int i;
+
+ if (!MACHINE_HAS_TOPOLOGY)
+ return;
+ tl_info = alloc_bootmem_pages(PAGE_SIZE);
+ info = tl_info;
+ store_topology(info);
+ pr_info("The CPU configuration topology of the machine is:");
+ for (i = 0; i < TOPOLOGY_NR_MAG; i++)
+ printk(KERN_CONT " %d", info->mag[i]);
+ printk(KERN_CONT " / %d\n", info->mnest);
+ alloc_masks(info, &core_info, 1);
+ alloc_masks(info, &book_info, 2);
+}
+
+static int cpu_management;
+
+static ssize_t dispatching_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ ssize_t count;
+
+ mutex_lock(&smp_cpu_state_mutex);
+ count = sprintf(buf, "%d\n", cpu_management);
+ mutex_unlock(&smp_cpu_state_mutex);
+ return count;
+}
+
+static ssize_t dispatching_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int val, rc;
+ char delim;
+
+ if (sscanf(buf, "%d %c", &val, &delim) != 1)
+ return -EINVAL;
+ if (val != 0 && val != 1)
+ return -EINVAL;
+ rc = 0;
+ get_online_cpus();
+ mutex_lock(&smp_cpu_state_mutex);
+ if (cpu_management == val)
+ goto out;
+ rc = topology_set_cpu_management(val);
+ if (rc)
+ goto out;
+ cpu_management = val;
+ topology_expect_change();
+out:
+ mutex_unlock(&smp_cpu_state_mutex);
+ put_online_cpus();
+ return rc ? rc : count;
+}
+static DEVICE_ATTR(dispatching, 0644, dispatching_show,
+ dispatching_store);
+
+static ssize_t cpu_polarization_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int cpu = dev->id;
+ ssize_t count;
+
+ mutex_lock(&smp_cpu_state_mutex);
+ switch (cpu_read_polarization(cpu)) {
+ case POLARIZATION_HRZ:
+ count = sprintf(buf, "horizontal\n");
+ break;
+ case POLARIZATION_VL:
+ count = sprintf(buf, "vertical:low\n");
+ break;
+ case POLARIZATION_VM:
+ count = sprintf(buf, "vertical:medium\n");
+ break;
+ case POLARIZATION_VH:
+ count = sprintf(buf, "vertical:high\n");
+ break;
+ default:
+ count = sprintf(buf, "unknown\n");
+ break;
+ }
+ mutex_unlock(&smp_cpu_state_mutex);
+ return count;
+}
+static DEVICE_ATTR(polarization, 0444, cpu_polarization_show, NULL);
+
+static struct attribute *topology_cpu_attrs[] = {
+ &dev_attr_polarization.attr,
+ NULL,
+};
+
+static struct attribute_group topology_cpu_attr_group = {
+ .attrs = topology_cpu_attrs,
+};
+
+int topology_cpu_init(struct cpu *cpu)
+{
+ return sysfs_create_group(&cpu->dev.kobj, &topology_cpu_attr_group);
+}
+
+static int __init topology_init(void)
+{
+ if (!MACHINE_HAS_TOPOLOGY) {
+ topology_update_polarization_simple();
+ goto out;
+ }
+ set_topology_timer();
+out:
+ update_cpu_core_map();
+ return device_create_file(cpu_subsys.dev_root, &dev_attr_dispatching);
+}
+device_initcall(topology_init);
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
new file mode 100644
index 00000000..77cdf423
--- /dev/null
+++ b/arch/s390/kernel/traps.c
@@ -0,0 +1,656 @@
+/*
+ * arch/s390/kernel/traps.c
+ *
+ * S390 version
+ * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
+ *
+ * Derived from "arch/i386/kernel/traps.c"
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * 'Traps.c' handles hardware traps and faults after we have saved some
+ * state in 'asm.s'.
+ */
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/seq_file.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/kdebug.h>
+#include <linux/kallsyms.h>
+#include <linux/reboot.h>
+#include <linux/kprobes.h>
+#include <linux/bug.h>
+#include <linux/utsname.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/atomic.h>
+#include <asm/mathemu.h>
+#include <asm/cpcmd.h>
+#include <asm/lowcore.h>
+#include <asm/debug.h>
+#include <asm/ipl.h>
+#include "entry.h"
+
+void (*pgm_check_table[128])(struct pt_regs *regs);
+
+int show_unhandled_signals = 1;
+
+#define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; })
+
+#ifndef CONFIG_64BIT
+#define LONG "%08lx "
+#define FOURLONG "%08lx %08lx %08lx %08lx\n"
+static int kstack_depth_to_print = 12;
+#else /* CONFIG_64BIT */
+#define LONG "%016lx "
+#define FOURLONG "%016lx %016lx %016lx %016lx\n"
+static int kstack_depth_to_print = 20;
+#endif /* CONFIG_64BIT */
+
+/*
+ * For show_trace we have tree different stack to consider:
+ * - the panic stack which is used if the kernel stack has overflown
+ * - the asynchronous interrupt stack (cpu related)
+ * - the synchronous kernel stack (process related)
+ * The stack trace can start at any of the three stack and can potentially
+ * touch all of them. The order is: panic stack, async stack, sync stack.
+ */
+static unsigned long
+__show_trace(unsigned long sp, unsigned long low, unsigned long high)
+{
+ struct stack_frame *sf;
+ struct pt_regs *regs;
+
+ while (1) {
+ sp = sp & PSW_ADDR_INSN;
+ if (sp < low || sp > high - sizeof(*sf))
+ return sp;
+ sf = (struct stack_frame *) sp;
+ printk("([<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN);
+ print_symbol("%s)\n", sf->gprs[8] & PSW_ADDR_INSN);
+ /* Follow the backchain. */
+ while (1) {
+ low = sp;
+ sp = sf->back_chain & PSW_ADDR_INSN;
+ if (!sp)
+ break;
+ if (sp <= low || sp > high - sizeof(*sf))
+ return sp;
+ sf = (struct stack_frame *) sp;
+ printk(" [<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN);
+ print_symbol("%s\n", sf->gprs[8] & PSW_ADDR_INSN);
+ }
+ /* Zero backchain detected, check for interrupt frame. */
+ sp = (unsigned long) (sf + 1);
+ if (sp <= low || sp > high - sizeof(*regs))
+ return sp;
+ regs = (struct pt_regs *) sp;
+ printk(" [<%016lx>] ", regs->psw.addr & PSW_ADDR_INSN);
+ print_symbol("%s\n", regs->psw.addr & PSW_ADDR_INSN);
+ low = sp;
+ sp = regs->gprs[15];
+ }
+}
+
+static void show_trace(struct task_struct *task, unsigned long *stack)
+{
+ register unsigned long __r15 asm ("15");
+ unsigned long sp;
+
+ sp = (unsigned long) stack;
+ if (!sp)
+ sp = task ? task->thread.ksp : __r15;
+ printk("Call Trace:\n");
+#ifdef CONFIG_CHECK_STACK
+ sp = __show_trace(sp, S390_lowcore.panic_stack - 4096,
+ S390_lowcore.panic_stack);
+#endif
+ sp = __show_trace(sp, S390_lowcore.async_stack - ASYNC_SIZE,
+ S390_lowcore.async_stack);
+ if (task)
+ __show_trace(sp, (unsigned long) task_stack_page(task),
+ (unsigned long) task_stack_page(task) + THREAD_SIZE);
+ else
+ __show_trace(sp, S390_lowcore.thread_info,
+ S390_lowcore.thread_info + THREAD_SIZE);
+ if (!task)
+ task = current;
+ debug_show_held_locks(task);
+}
+
+void show_stack(struct task_struct *task, unsigned long *sp)
+{
+ register unsigned long * __r15 asm ("15");
+ unsigned long *stack;
+ int i;
+
+ if (!sp)
+ stack = task ? (unsigned long *) task->thread.ksp : __r15;
+ else
+ stack = sp;
+
+ for (i = 0; i < kstack_depth_to_print; i++) {
+ if (((addr_t) stack & (THREAD_SIZE-1)) == 0)
+ break;
+ if ((i * sizeof(long) % 32) == 0)
+ printk("%s ", i == 0 ? "" : "\n");
+ printk(LONG, *stack++);
+ }
+ printk("\n");
+ show_trace(task, sp);
+}
+
+static void show_last_breaking_event(struct pt_regs *regs)
+{
+#ifdef CONFIG_64BIT
+ printk("Last Breaking-Event-Address:\n");
+ printk(" [<%016lx>] ", regs->args[0] & PSW_ADDR_INSN);
+ print_symbol("%s\n", regs->args[0] & PSW_ADDR_INSN);
+#endif
+}
+
+/*
+ * The architecture-independent dump_stack generator
+ */
+void dump_stack(void)
+{
+ printk("CPU: %d %s %s %.*s\n",
+ task_thread_info(current)->cpu, print_tainted(),
+ init_utsname()->release,
+ (int)strcspn(init_utsname()->version, " "),
+ init_utsname()->version);
+ printk("Process %s (pid: %d, task: %p, ksp: %p)\n",
+ current->comm, current->pid, current,
+ (void *) current->thread.ksp);
+ show_stack(NULL, NULL);
+}
+EXPORT_SYMBOL(dump_stack);
+
+static inline int mask_bits(struct pt_regs *regs, unsigned long bits)
+{
+ return (regs->psw.mask & bits) / ((~bits + 1) & bits);
+}
+
+void show_registers(struct pt_regs *regs)
+{
+ char *mode;
+
+ mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl";
+ printk("%s PSW : %p %p",
+ mode, (void *) regs->psw.mask,
+ (void *) regs->psw.addr);
+ print_symbol(" (%s)\n", regs->psw.addr & PSW_ADDR_INSN);
+ printk(" R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x "
+ "P:%x AS:%x CC:%x PM:%x", mask_bits(regs, PSW_MASK_PER),
+ mask_bits(regs, PSW_MASK_DAT), mask_bits(regs, PSW_MASK_IO),
+ mask_bits(regs, PSW_MASK_EXT), mask_bits(regs, PSW_MASK_KEY),
+ mask_bits(regs, PSW_MASK_MCHECK), mask_bits(regs, PSW_MASK_WAIT),
+ mask_bits(regs, PSW_MASK_PSTATE), mask_bits(regs, PSW_MASK_ASC),
+ mask_bits(regs, PSW_MASK_CC), mask_bits(regs, PSW_MASK_PM));
+#ifdef CONFIG_64BIT
+ printk(" EA:%x", mask_bits(regs, PSW_MASK_EA | PSW_MASK_BA));
+#endif
+ printk("\n%s GPRS: " FOURLONG, mode,
+ regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]);
+ printk(" " FOURLONG,
+ regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]);
+ printk(" " FOURLONG,
+ regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]);
+ printk(" " FOURLONG,
+ regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]);
+
+ show_code(regs);
+}
+
+void show_regs(struct pt_regs *regs)
+{
+ print_modules();
+ printk("CPU: %d %s %s %.*s\n",
+ task_thread_info(current)->cpu, print_tainted(),
+ init_utsname()->release,
+ (int)strcspn(init_utsname()->version, " "),
+ init_utsname()->version);
+ printk("Process %s (pid: %d, task: %p, ksp: %p)\n",
+ current->comm, current->pid, current,
+ (void *) current->thread.ksp);
+ show_registers(regs);
+ /* Show stack backtrace if pt_regs is from kernel mode */
+ if (!(regs->psw.mask & PSW_MASK_PSTATE))
+ show_trace(NULL, (unsigned long *) regs->gprs[15]);
+ show_last_breaking_event(regs);
+}
+
+static DEFINE_SPINLOCK(die_lock);
+
+void die(struct pt_regs *regs, const char *str)
+{
+ static int die_counter;
+
+ oops_enter();
+ lgr_info_log();
+ debug_stop_all();
+ console_verbose();
+ spin_lock_irq(&die_lock);
+ bust_spinlocks(1);
+ printk("%s: %04x [#%d] ", str, regs->int_code & 0xffff, ++die_counter);
+#ifdef CONFIG_PREEMPT
+ printk("PREEMPT ");
+#endif
+#ifdef CONFIG_SMP
+ printk("SMP ");
+#endif
+#ifdef CONFIG_DEBUG_PAGEALLOC
+ printk("DEBUG_PAGEALLOC");
+#endif
+ printk("\n");
+ notify_die(DIE_OOPS, str, regs, 0, regs->int_code & 0xffff, SIGSEGV);
+ show_regs(regs);
+ bust_spinlocks(0);
+ add_taint(TAINT_DIE);
+ spin_unlock_irq(&die_lock);
+ if (in_interrupt())
+ panic("Fatal exception in interrupt");
+ if (panic_on_oops)
+ panic("Fatal exception: panic_on_oops");
+ oops_exit();
+ do_exit(SIGSEGV);
+}
+
+static inline void report_user_fault(struct pt_regs *regs, int signr)
+{
+ if ((task_pid_nr(current) > 1) && !show_unhandled_signals)
+ return;
+ if (!unhandled_signal(current, signr))
+ return;
+ if (!printk_ratelimit())
+ return;
+ printk("User process fault: interruption code 0x%X ", regs->int_code);
+ print_vma_addr("in ", regs->psw.addr & PSW_ADDR_INSN);
+ printk("\n");
+ show_regs(regs);
+}
+
+int is_valid_bugaddr(unsigned long addr)
+{
+ return 1;
+}
+
+static inline void __user *get_psw_address(struct pt_regs *regs)
+{
+ return (void __user *)
+ ((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN);
+}
+
+static void __kprobes do_trap(struct pt_regs *regs,
+ int si_signo, int si_code, char *str)
+{
+ siginfo_t info;
+
+ if (notify_die(DIE_TRAP, str, regs, 0,
+ regs->int_code, si_signo) == NOTIFY_STOP)
+ return;
+
+ if (regs->psw.mask & PSW_MASK_PSTATE) {
+ info.si_signo = si_signo;
+ info.si_errno = 0;
+ info.si_code = si_code;
+ info.si_addr = get_psw_address(regs);
+ force_sig_info(si_signo, &info, current);
+ report_user_fault(regs, si_signo);
+ } else {
+ const struct exception_table_entry *fixup;
+ fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
+ if (fixup)
+ regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
+ else {
+ enum bug_trap_type btt;
+
+ btt = report_bug(regs->psw.addr & PSW_ADDR_INSN, regs);
+ if (btt == BUG_TRAP_TYPE_WARN)
+ return;
+ die(regs, str);
+ }
+ }
+}
+
+void __kprobes do_per_trap(struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0, SIGTRAP) == NOTIFY_STOP)
+ return;
+ if (!current->ptrace)
+ return;
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TRAP_HWBKPT;
+ info.si_addr =
+ (void __force __user *) current->thread.per_event.address;
+ force_sig_info(SIGTRAP, &info, current);
+}
+
+static void default_trap_handler(struct pt_regs *regs)
+{
+ if (regs->psw.mask & PSW_MASK_PSTATE) {
+ report_user_fault(regs, SIGSEGV);
+ do_exit(SIGSEGV);
+ } else
+ die(regs, "Unknown program exception");
+}
+
+#define DO_ERROR_INFO(name, signr, sicode, str) \
+static void name(struct pt_regs *regs) \
+{ \
+ do_trap(regs, signr, sicode, str); \
+}
+
+DO_ERROR_INFO(addressing_exception, SIGILL, ILL_ILLADR,
+ "addressing exception")
+DO_ERROR_INFO(execute_exception, SIGILL, ILL_ILLOPN,
+ "execute exception")
+DO_ERROR_INFO(divide_exception, SIGFPE, FPE_INTDIV,
+ "fixpoint divide exception")
+DO_ERROR_INFO(overflow_exception, SIGFPE, FPE_INTOVF,
+ "fixpoint overflow exception")
+DO_ERROR_INFO(hfp_overflow_exception, SIGFPE, FPE_FLTOVF,
+ "HFP overflow exception")
+DO_ERROR_INFO(hfp_underflow_exception, SIGFPE, FPE_FLTUND,
+ "HFP underflow exception")
+DO_ERROR_INFO(hfp_significance_exception, SIGFPE, FPE_FLTRES,
+ "HFP significance exception")
+DO_ERROR_INFO(hfp_divide_exception, SIGFPE, FPE_FLTDIV,
+ "HFP divide exception")
+DO_ERROR_INFO(hfp_sqrt_exception, SIGFPE, FPE_FLTINV,
+ "HFP square root exception")
+DO_ERROR_INFO(operand_exception, SIGILL, ILL_ILLOPN,
+ "operand exception")
+DO_ERROR_INFO(privileged_op, SIGILL, ILL_PRVOPC,
+ "privileged operation")
+DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
+ "special operation exception")
+DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN,
+ "translation exception")
+
+static inline void do_fp_trap(struct pt_regs *regs, int fpc)
+{
+ int si_code = 0;
+ /* FPC[2] is Data Exception Code */
+ if ((fpc & 0x00000300) == 0) {
+ /* bits 6 and 7 of DXC are 0 iff IEEE exception */
+ if (fpc & 0x8000) /* invalid fp operation */
+ si_code = FPE_FLTINV;
+ else if (fpc & 0x4000) /* div by 0 */
+ si_code = FPE_FLTDIV;
+ else if (fpc & 0x2000) /* overflow */
+ si_code = FPE_FLTOVF;
+ else if (fpc & 0x1000) /* underflow */
+ si_code = FPE_FLTUND;
+ else if (fpc & 0x0800) /* inexact */
+ si_code = FPE_FLTRES;
+ }
+ do_trap(regs, SIGFPE, si_code, "floating point exception");
+}
+
+static void __kprobes illegal_op(struct pt_regs *regs)
+{
+ siginfo_t info;
+ __u8 opcode[6];
+ __u16 __user *location;
+ int signal = 0;
+
+ location = get_psw_address(regs);
+
+ if (regs->psw.mask & PSW_MASK_PSTATE) {
+ if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
+ return;
+ if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
+ if (current->ptrace) {
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TRAP_BRKPT;
+ info.si_addr = location;
+ force_sig_info(SIGTRAP, &info, current);
+ } else
+ signal = SIGILL;
+#ifdef CONFIG_MATHEMU
+ } else if (opcode[0] == 0xb3) {
+ if (get_user(*((__u16 *) (opcode+2)), location+1))
+ return;
+ signal = math_emu_b3(opcode, regs);
+ } else if (opcode[0] == 0xed) {
+ if (get_user(*((__u32 *) (opcode+2)),
+ (__u32 __user *)(location+1)))
+ return;
+ signal = math_emu_ed(opcode, regs);
+ } else if (*((__u16 *) opcode) == 0xb299) {
+ if (get_user(*((__u16 *) (opcode+2)), location+1))
+ return;
+ signal = math_emu_srnm(opcode, regs);
+ } else if (*((__u16 *) opcode) == 0xb29c) {
+ if (get_user(*((__u16 *) (opcode+2)), location+1))
+ return;
+ signal = math_emu_stfpc(opcode, regs);
+ } else if (*((__u16 *) opcode) == 0xb29d) {
+ if (get_user(*((__u16 *) (opcode+2)), location+1))
+ return;
+ signal = math_emu_lfpc(opcode, regs);
+#endif
+ } else
+ signal = SIGILL;
+ } else {
+ /*
+ * If we get an illegal op in kernel mode, send it through the
+ * kprobes notifier. If kprobes doesn't pick it up, SIGILL
+ */
+ if (notify_die(DIE_BPT, "bpt", regs, 0,
+ 3, SIGTRAP) != NOTIFY_STOP)
+ signal = SIGILL;
+ }
+
+#ifdef CONFIG_MATHEMU
+ if (signal == SIGFPE)
+ do_fp_trap(regs, current->thread.fp_regs.fpc);
+ else if (signal == SIGSEGV)
+ do_trap(regs, signal, SEGV_MAPERR, "user address fault");
+ else
+#endif
+ if (signal)
+ do_trap(regs, signal, ILL_ILLOPC, "illegal operation");
+}
+
+
+#ifdef CONFIG_MATHEMU
+void specification_exception(struct pt_regs *regs)
+{
+ __u8 opcode[6];
+ __u16 __user *location = NULL;
+ int signal = 0;
+
+ location = (__u16 __user *) get_psw_address(regs);
+
+ if (regs->psw.mask & PSW_MASK_PSTATE) {
+ get_user(*((__u16 *) opcode), location);
+ switch (opcode[0]) {
+ case 0x28: /* LDR Rx,Ry */
+ signal = math_emu_ldr(opcode);
+ break;
+ case 0x38: /* LER Rx,Ry */
+ signal = math_emu_ler(opcode);
+ break;
+ case 0x60: /* STD R,D(X,B) */
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_std(opcode, regs);
+ break;
+ case 0x68: /* LD R,D(X,B) */
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_ld(opcode, regs);
+ break;
+ case 0x70: /* STE R,D(X,B) */
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_ste(opcode, regs);
+ break;
+ case 0x78: /* LE R,D(X,B) */
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_le(opcode, regs);
+ break;
+ default:
+ signal = SIGILL;
+ break;
+ }
+ } else
+ signal = SIGILL;
+
+ if (signal == SIGFPE)
+ do_fp_trap(regs, current->thread.fp_regs.fpc);
+ else if (signal)
+ do_trap(regs, signal, ILL_ILLOPN, "specification exception");
+}
+#else
+DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
+ "specification exception");
+#endif
+
+static void data_exception(struct pt_regs *regs)
+{
+ __u16 __user *location;
+ int signal = 0;
+
+ location = get_psw_address(regs);
+
+ if (MACHINE_HAS_IEEE)
+ asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
+
+#ifdef CONFIG_MATHEMU
+ else if (regs->psw.mask & PSW_MASK_PSTATE) {
+ __u8 opcode[6];
+ get_user(*((__u16 *) opcode), location);
+ switch (opcode[0]) {
+ case 0x28: /* LDR Rx,Ry */
+ signal = math_emu_ldr(opcode);
+ break;
+ case 0x38: /* LER Rx,Ry */
+ signal = math_emu_ler(opcode);
+ break;
+ case 0x60: /* STD R,D(X,B) */
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_std(opcode, regs);
+ break;
+ case 0x68: /* LD R,D(X,B) */
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_ld(opcode, regs);
+ break;
+ case 0x70: /* STE R,D(X,B) */
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_ste(opcode, regs);
+ break;
+ case 0x78: /* LE R,D(X,B) */
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_le(opcode, regs);
+ break;
+ case 0xb3:
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_b3(opcode, regs);
+ break;
+ case 0xed:
+ get_user(*((__u32 *) (opcode+2)),
+ (__u32 __user *)(location+1));
+ signal = math_emu_ed(opcode, regs);
+ break;
+ case 0xb2:
+ if (opcode[1] == 0x99) {
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_srnm(opcode, regs);
+ } else if (opcode[1] == 0x9c) {
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_stfpc(opcode, regs);
+ } else if (opcode[1] == 0x9d) {
+ get_user(*((__u16 *) (opcode+2)), location+1);
+ signal = math_emu_lfpc(opcode, regs);
+ } else
+ signal = SIGILL;
+ break;
+ default:
+ signal = SIGILL;
+ break;
+ }
+ }
+#endif
+ if (current->thread.fp_regs.fpc & FPC_DXC_MASK)
+ signal = SIGFPE;
+ else
+ signal = SIGILL;
+ if (signal == SIGFPE)
+ do_fp_trap(regs, current->thread.fp_regs.fpc);
+ else if (signal)
+ do_trap(regs, signal, ILL_ILLOPN, "data exception");
+}
+
+static void space_switch_exception(struct pt_regs *regs)
+{
+ /* Set user psw back to home space mode. */
+ if (regs->psw.mask & PSW_MASK_PSTATE)
+ regs->psw.mask |= PSW_ASC_HOME;
+ /* Send SIGILL. */
+ do_trap(regs, SIGILL, ILL_PRVOPC, "space switch event");
+}
+
+void __kprobes kernel_stack_overflow(struct pt_regs * regs)
+{
+ bust_spinlocks(1);
+ printk("Kernel stack overflow.\n");
+ show_regs(regs);
+ bust_spinlocks(0);
+ panic("Corrupt kernel stack, can't continue.");
+}
+
+/* init is done in lowcore.S and head.S */
+
+void __init trap_init(void)
+{
+ int i;
+
+ for (i = 0; i < 128; i++)
+ pgm_check_table[i] = &default_trap_handler;
+ pgm_check_table[1] = &illegal_op;
+ pgm_check_table[2] = &privileged_op;
+ pgm_check_table[3] = &execute_exception;
+ pgm_check_table[4] = &do_protection_exception;
+ pgm_check_table[5] = &addressing_exception;
+ pgm_check_table[6] = &specification_exception;
+ pgm_check_table[7] = &data_exception;
+ pgm_check_table[8] = &overflow_exception;
+ pgm_check_table[9] = &divide_exception;
+ pgm_check_table[0x0A] = &overflow_exception;
+ pgm_check_table[0x0B] = &divide_exception;
+ pgm_check_table[0x0C] = &hfp_overflow_exception;
+ pgm_check_table[0x0D] = &hfp_underflow_exception;
+ pgm_check_table[0x0E] = &hfp_significance_exception;
+ pgm_check_table[0x0F] = &hfp_divide_exception;
+ pgm_check_table[0x10] = &do_dat_exception;
+ pgm_check_table[0x11] = &do_dat_exception;
+ pgm_check_table[0x12] = &translation_exception;
+ pgm_check_table[0x13] = &special_op_exception;
+#ifdef CONFIG_64BIT
+ pgm_check_table[0x38] = &do_asce_exception;
+ pgm_check_table[0x39] = &do_dat_exception;
+ pgm_check_table[0x3A] = &do_dat_exception;
+ pgm_check_table[0x3B] = &do_dat_exception;
+#endif /* CONFIG_64BIT */
+ pgm_check_table[0x15] = &operand_exception;
+ pgm_check_table[0x1C] = &space_switch_exception;
+ pgm_check_table[0x1D] = &hfp_sqrt_exception;
+ /* Enable machine checks early. */
+ local_mcck_enable();
+}
diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c
new file mode 100644
index 00000000..ea5590fd
--- /dev/null
+++ b/arch/s390/kernel/vdso.c
@@ -0,0 +1,333 @@
+/*
+ * vdso setup for s390
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/elf.h>
+#include <linux/security.h>
+#include <linux/bootmem.h>
+#include <linux/compat.h>
+#include <asm/asm-offsets.h>
+#include <asm/pgtable.h>
+#include <asm/processor.h>
+#include <asm/mmu.h>
+#include <asm/mmu_context.h>
+#include <asm/sections.h>
+#include <asm/vdso.h>
+#include <asm/facility.h>
+
+#if defined(CONFIG_32BIT) || defined(CONFIG_COMPAT)
+extern char vdso32_start, vdso32_end;
+static void *vdso32_kbase = &vdso32_start;
+static unsigned int vdso32_pages;
+static struct page **vdso32_pagelist;
+#endif
+
+#ifdef CONFIG_64BIT
+extern char vdso64_start, vdso64_end;
+static void *vdso64_kbase = &vdso64_start;
+static unsigned int vdso64_pages;
+static struct page **vdso64_pagelist;
+#endif /* CONFIG_64BIT */
+
+/*
+ * Should the kernel map a VDSO page into processes and pass its
+ * address down to glibc upon exec()?
+ */
+unsigned int __read_mostly vdso_enabled = 1;
+
+static int __init vdso_setup(char *s)
+{
+ unsigned long val;
+ int rc;
+
+ rc = 0;
+ if (strncmp(s, "on", 3) == 0)
+ vdso_enabled = 1;
+ else if (strncmp(s, "off", 4) == 0)
+ vdso_enabled = 0;
+ else {
+ rc = strict_strtoul(s, 0, &val);
+ vdso_enabled = rc ? 0 : !!val;
+ }
+ return !rc;
+}
+__setup("vdso=", vdso_setup);
+
+/*
+ * The vdso data page
+ */
+static union {
+ struct vdso_data data;
+ u8 page[PAGE_SIZE];
+} vdso_data_store __page_aligned_data;
+struct vdso_data *vdso_data = &vdso_data_store.data;
+
+/*
+ * Setup vdso data page.
+ */
+static void vdso_init_data(struct vdso_data *vd)
+{
+ vd->ectg_available = user_mode != HOME_SPACE_MODE && test_facility(31);
+}
+
+#ifdef CONFIG_64BIT
+/*
+ * Allocate/free per cpu vdso data.
+ */
+#define SEGMENT_ORDER 2
+
+int vdso_alloc_per_cpu(struct _lowcore *lowcore)
+{
+ unsigned long segment_table, page_table, page_frame;
+ u32 *psal, *aste;
+ int i;
+
+ lowcore->vdso_per_cpu_data = __LC_PASTE;
+
+ if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
+ return 0;
+
+ segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER);
+ page_table = get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ page_frame = get_zeroed_page(GFP_KERNEL);
+ if (!segment_table || !page_table || !page_frame)
+ goto out;
+
+ clear_table((unsigned long *) segment_table, _SEGMENT_ENTRY_EMPTY,
+ PAGE_SIZE << SEGMENT_ORDER);
+ clear_table((unsigned long *) page_table, _PAGE_TYPE_EMPTY,
+ 256*sizeof(unsigned long));
+
+ *(unsigned long *) segment_table = _SEGMENT_ENTRY + page_table;
+ *(unsigned long *) page_table = _PAGE_RO + page_frame;
+
+ psal = (u32 *) (page_table + 256*sizeof(unsigned long));
+ aste = psal + 32;
+
+ for (i = 4; i < 32; i += 4)
+ psal[i] = 0x80000000;
+
+ lowcore->paste[4] = (u32)(addr_t) psal;
+ psal[0] = 0x20000000;
+ psal[2] = (u32)(addr_t) aste;
+ *(unsigned long *) (aste + 2) = segment_table +
+ _ASCE_TABLE_LENGTH + _ASCE_USER_BITS + _ASCE_TYPE_SEGMENT;
+ aste[4] = (u32)(addr_t) psal;
+ lowcore->vdso_per_cpu_data = page_frame;
+
+ return 0;
+
+out:
+ free_page(page_frame);
+ free_page(page_table);
+ free_pages(segment_table, SEGMENT_ORDER);
+ return -ENOMEM;
+}
+
+void vdso_free_per_cpu(struct _lowcore *lowcore)
+{
+ unsigned long segment_table, page_table, page_frame;
+ u32 *psal, *aste;
+
+ if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
+ return;
+
+ psal = (u32 *)(addr_t) lowcore->paste[4];
+ aste = (u32 *)(addr_t) psal[2];
+ segment_table = *(unsigned long *)(aste + 2) & PAGE_MASK;
+ page_table = *(unsigned long *) segment_table;
+ page_frame = *(unsigned long *) page_table;
+
+ free_page(page_frame);
+ free_page(page_table);
+ free_pages(segment_table, SEGMENT_ORDER);
+}
+
+static void vdso_init_cr5(void)
+{
+ unsigned long cr5;
+
+ if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
+ return;
+ cr5 = offsetof(struct _lowcore, paste);
+ __ctl_load(cr5, 5, 5);
+}
+#endif /* CONFIG_64BIT */
+
+/*
+ * This is called from binfmt_elf, we create the special vma for the
+ * vDSO and insert it into the mm struct tree
+ */
+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
+{
+ struct mm_struct *mm = current->mm;
+ struct page **vdso_pagelist;
+ unsigned long vdso_pages;
+ unsigned long vdso_base;
+ int rc;
+
+ if (!vdso_enabled)
+ return 0;
+ /*
+ * Only map the vdso for dynamically linked elf binaries.
+ */
+ if (!uses_interp)
+ return 0;
+
+#ifdef CONFIG_64BIT
+ vdso_pagelist = vdso64_pagelist;
+ vdso_pages = vdso64_pages;
+#ifdef CONFIG_COMPAT
+ if (is_compat_task()) {
+ vdso_pagelist = vdso32_pagelist;
+ vdso_pages = vdso32_pages;
+ }
+#endif
+#else
+ vdso_pagelist = vdso32_pagelist;
+ vdso_pages = vdso32_pages;
+#endif
+
+ /*
+ * vDSO has a problem and was disabled, just don't "enable" it for
+ * the process
+ */
+ if (vdso_pages == 0)
+ return 0;
+
+ current->mm->context.vdso_base = 0;
+
+ /*
+ * pick a base address for the vDSO in process space. We try to put
+ * it at vdso_base which is the "natural" base for it, but we might
+ * fail and end up putting it elsewhere.
+ */
+ down_write(&mm->mmap_sem);
+ vdso_base = get_unmapped_area(NULL, 0, vdso_pages << PAGE_SHIFT, 0, 0);
+ if (IS_ERR_VALUE(vdso_base)) {
+ rc = vdso_base;
+ goto out_up;
+ }
+
+ /*
+ * Put vDSO base into mm struct. We need to do this before calling
+ * install_special_mapping or the perf counter mmap tracking code
+ * will fail to recognise it as a vDSO (since arch_vma_name fails).
+ */
+ current->mm->context.vdso_base = vdso_base;
+
+ /*
+ * our vma flags don't have VM_WRITE so by default, the process
+ * isn't allowed to write those pages.
+ * gdb can break that with ptrace interface, and thus trigger COW
+ * on those pages but it's then your responsibility to never do that
+ * on the "data" page of the vDSO or you'll stop getting kernel
+ * updates and your nice userland gettimeofday will be totally dead.
+ * It's fine to use that for setting breakpoints in the vDSO code
+ * pages though.
+ */
+ rc = install_special_mapping(mm, vdso_base, vdso_pages << PAGE_SHIFT,
+ VM_READ|VM_EXEC|
+ VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
+ vdso_pagelist);
+ if (rc)
+ current->mm->context.vdso_base = 0;
+out_up:
+ up_write(&mm->mmap_sem);
+ return rc;
+}
+
+const char *arch_vma_name(struct vm_area_struct *vma)
+{
+ if (vma->vm_mm && vma->vm_start == vma->vm_mm->context.vdso_base)
+ return "[vdso]";
+ return NULL;
+}
+
+static int __init vdso_init(void)
+{
+ int i;
+
+ if (!vdso_enabled)
+ return 0;
+ vdso_init_data(vdso_data);
+#if defined(CONFIG_32BIT) || defined(CONFIG_COMPAT)
+ /* 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 = kzalloc(sizeof(struct page *) * (vdso32_pages + 1),
+ 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);
+ ClearPageReserved(pg);
+ get_page(pg);
+ vdso32_pagelist[i] = pg;
+ }
+ vdso32_pagelist[vdso32_pages - 1] = virt_to_page(vdso_data);
+ vdso32_pagelist[vdso32_pages] = NULL;
+#endif
+
+#ifdef CONFIG_64BIT
+ /* Calculate the size of the 64 bit vDSO */
+ vdso64_pages = ((&vdso64_end - &vdso64_start
+ + PAGE_SIZE - 1) >> PAGE_SHIFT) + 1;
+
+ /* Make sure pages are in the correct state */
+ vdso64_pagelist = kzalloc(sizeof(struct page *) * (vdso64_pages + 1),
+ GFP_KERNEL);
+ BUG_ON(vdso64_pagelist == NULL);
+ for (i = 0; i < vdso64_pages - 1; i++) {
+ struct page *pg = virt_to_page(vdso64_kbase + i*PAGE_SIZE);
+ ClearPageReserved(pg);
+ get_page(pg);
+ vdso64_pagelist[i] = pg;
+ }
+ vdso64_pagelist[vdso64_pages - 1] = virt_to_page(vdso_data);
+ vdso64_pagelist[vdso64_pages] = NULL;
+ if (vdso_alloc_per_cpu(&S390_lowcore))
+ BUG();
+ vdso_init_cr5();
+#endif /* CONFIG_64BIT */
+
+ get_page(virt_to_page(vdso_data));
+
+ smp_wmb();
+
+ return 0;
+}
+early_initcall(vdso_init);
+
+int in_gate_area_no_mm(unsigned long addr)
+{
+ return 0;
+}
+
+int in_gate_area(struct mm_struct *mm, unsigned long addr)
+{
+ return 0;
+}
+
+struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
+{
+ return NULL;
+}
diff --git a/arch/s390/kernel/vdso32/Makefile b/arch/s390/kernel/vdso32/Makefile
new file mode 100644
index 00000000..8ad2b34a
--- /dev/null
+++ b/arch/s390/kernel/vdso32/Makefile
@@ -0,0 +1,58 @@
+# List of files in the vdso, has to be asm only for now
+
+obj-vdso32 = gettimeofday.o clock_getres.o clock_gettime.o note.o
+
+# Build rules
+
+targets := $(obj-vdso32) vdso32.so vdso32.so.dbg
+obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32))
+
+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 \
+ $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
+
+$(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 for VDSO code
+GCOV_PROFILE := 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)
+ $(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
+ $(call if_changed_dep,vdso32as)
+
+# actual build commands
+quiet_cmd_vdso32ld = VDSO32L $@
+ cmd_vdso32ld = $(CC) $(c_flags) -Wl,-T $^ -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
diff --git a/arch/s390/kernel/vdso32/clock_getres.S b/arch/s390/kernel/vdso32/clock_getres.S
new file mode 100644
index 00000000..36aaa25d
--- /dev/null
+++ b/arch/s390/kernel/vdso32/clock_getres.S
@@ -0,0 +1,39 @@
+/*
+ * 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)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_clock_getres
+ .type __kernel_clock_getres,@function
+__kernel_clock_getres:
+ .cfi_startproc
+ chi %r2,__CLOCK_REALTIME
+ je 0f
+ chi %r2,__CLOCK_MONOTONIC
+ jne 3f
+0: ltr %r3,%r3
+ jz 2f /* res == NULL */
+ basr %r1,0
+1: l %r0,4f-1b(%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
+4: .long __CLOCK_REALTIME_RES
+ .cfi_endproc
+ .size __kernel_clock_getres,.-__kernel_clock_getres
diff --git a/arch/s390/kernel/vdso32/clock_gettime.S b/arch/s390/kernel/vdso32/clock_gettime.S
new file mode 100644
index 00000000..b2224e0b
--- /dev/null
+++ b/arch/s390/kernel/vdso32/clock_gettime.S
@@ -0,0 +1,128 @@
+/*
+ * 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)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_clock_gettime
+ .type __kernel_clock_gettime,@function
+__kernel_clock_gettime:
+ .cfi_startproc
+ basr %r5,0
+0: al %r5,21f-0b(%r5) /* get &_vdso_data */
+ chi %r2,__CLOCK_REALTIME
+ je 10f
+ chi %r2,__CLOCK_MONOTONIC
+ jne 19f
+
+ /* CLOCK_MONOTONIC */
+ ltr %r3,%r3
+ jz 9f /* tp == NULL */
+1: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
+ tml %r4,0x0001 /* pending update ? loop */
+ jnz 1b
+ stck 24(%r15) /* Store TOD clock */
+ lm %r0,%r1,24(%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_NTP_MULT(%r5) /* cyc2ns(clock,cycle_delta) */
+ lr %r2,%r0
+ l %r0,__VDSO_NTP_MULT(%r5)
+ ltr %r1,%r1
+ mr %r0,%r0
+ jnm 3f
+ a %r0,__VDSO_NTP_MULT(%r5)
+3: alr %r0,%r2
+ srdl %r0,12
+ al %r0,__VDSO_XTIME_NSEC(%r5) /* + xtime */
+ al %r1,__VDSO_XTIME_NSEC+4(%r5)
+ brc 12,4f
+ ahi %r0,1
+4: l %r2,__VDSO_XTIME_SEC+4(%r5)
+ al %r0,__VDSO_WTOM_NSEC(%r5) /* + wall_to_monotonic */
+ al %r1,__VDSO_WTOM_NSEC+4(%r5)
+ brc 12,5f
+ ahi %r0,1
+5: al %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 */
+9: lhi %r2,0
+ br %r14
+
+ /* CLOCK_REALTIME */
+10: ltr %r3,%r3 /* tp == NULL */
+ jz 18f
+11: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */
+ tml %r4,0x0001 /* pending update ? loop */
+ jnz 11b
+ stck 24(%r15) /* Store TOD clock */
+ lm %r0,%r1,24(%r15)
+ 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_NTP_MULT(%r5) /* cyc2ns(clock,cycle_delta) */
+ lr %r2,%r0
+ l %r0,__VDSO_NTP_MULT(%r5)
+ ltr %r1,%r1
+ mr %r0,%r0
+ jnm 13f
+ a %r0,__VDSO_NTP_MULT(%r5)
+13: alr %r0,%r2
+ srdl %r0,12
+ al %r0,__VDSO_XTIME_NSEC(%r5) /* + xtime */
+ al %r1,__VDSO_XTIME_NSEC+4(%r5)
+ brc 12,14f
+ ahi %r0,1
+14: 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 */
+18: lhi %r2,0
+ br %r14
+
+ /* Fallback to system call */
+19: lhi %r1,__NR_clock_gettime
+ svc 0
+ br %r14
+
+20: .long 1000000000
+21: .long _vdso_data - 0b
+ .cfi_endproc
+ .size __kernel_clock_gettime,.-__kernel_clock_gettime
diff --git a/arch/s390/kernel/vdso32/gettimeofday.S b/arch/s390/kernel/vdso32/gettimeofday.S
new file mode 100644
index 00000000..2d363317
--- /dev/null
+++ b/arch/s390/kernel/vdso32/gettimeofday.S
@@ -0,0 +1,78 @@
+/*
+ * 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)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_gettimeofday
+ .type __kernel_gettimeofday,@function
+__kernel_gettimeofday:
+ .cfi_startproc
+ 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
+ stck 24(%r15) /* Store TOD clock */
+ lm %r0,%r1,24(%r15)
+ 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_NTP_MULT(%r5) /* cyc2ns(clock,cycle_delta) */
+ st %r0,24(%r15)
+ l %r0,__VDSO_NTP_MULT(%r5)
+ ltr %r1,%r1
+ mr %r0,%r0
+ jnm 4f
+ a %r0,__VDSO_NTP_MULT(%r5)
+4: al %r0,24(%r15)
+ srdl %r0,12
+ al %r0,__VDSO_XTIME_NSEC(%r5) /* + xtime */
+ al %r1,__VDSO_XTIME_NSEC+4(%r5)
+ brc 12,5f
+ ahi %r0,1
+5: mvc 24(4,%r15),__VDSO_XTIME_SEC+4(%r5)
+ cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */
+ jne 1b
+ l %r4,24(%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
+ br %r14
+11: .long 1000000000
+12: .long 274877907
+13: .long _vdso_data - 0b
+ .cfi_endproc
+ .size __kernel_gettimeofday,.-__kernel_gettimeofday
diff --git a/arch/s390/kernel/vdso32/note.S b/arch/s390/kernel/vdso32/note.S
new file mode 100644
index 00000000..79a071e4
--- /dev/null
+++ b/arch/s390/kernel/vdso32/note.S
@@ -0,0 +1,12 @@
+/*
+ * 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
diff --git a/arch/s390/kernel/vdso32/vdso32.lds.S b/arch/s390/kernel/vdso32/vdso32.lds.S
new file mode 100644
index 00000000..a8c379fa
--- /dev/null
+++ b/arch/s390/kernel/vdso32/vdso32.lds.S
@@ -0,0 +1,138 @@
+/*
+ * This is the infamous ld script for the 32 bits vdso
+ * library
+ */
+#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(4096);
+ 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;
+
+ local: *;
+ };
+}
diff --git a/arch/s390/kernel/vdso32/vdso32_wrapper.S b/arch/s390/kernel/vdso32/vdso32_wrapper.S
new file mode 100644
index 00000000..ae42f8ce
--- /dev/null
+++ b/arch/s390/kernel/vdso32/vdso32_wrapper.S
@@ -0,0 +1,14 @@
+#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
diff --git a/arch/s390/kernel/vdso64/Makefile b/arch/s390/kernel/vdso64/Makefile
new file mode 100644
index 00000000..2a8ddfd1
--- /dev/null
+++ b/arch/s390/kernel/vdso64/Makefile
@@ -0,0 +1,58 @@
+# List of files in the vdso, has to be asm only for now
+
+obj-vdso64 = gettimeofday.o clock_getres.o clock_gettime.o note.o
+
+# Build rules
+
+targets := $(obj-vdso64) vdso64.so vdso64.so.dbg
+obj-vdso64 := $(addprefix $(obj)/, $(obj-vdso64))
+
+KBUILD_AFLAGS_64 := $(filter-out -m64,$(KBUILD_AFLAGS))
+KBUILD_AFLAGS_64 += -m64 -s
+
+KBUILD_CFLAGS_64 := $(filter-out -m64,$(KBUILD_CFLAGS))
+KBUILD_CFLAGS_64 += -m64 -fPIC -shared -fno-common -fno-builtin
+KBUILD_CFLAGS_64 += -nostdlib -Wl,-soname=linux-vdso64.so.1 \
+ $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
+
+$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_64)
+$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_64)
+
+obj-y += vdso64_wrapper.o
+extra-y += vdso64.lds
+CPPFLAGS_vdso64.lds += -P -C -U$(ARCH)
+
+# Disable gcov profiling for VDSO code
+GCOV_PROFILE := n
+
+# Force dependency (incbin is bad)
+$(obj)/vdso64_wrapper.o : $(obj)/vdso64.so
+
+# link rule for the .so file, .lds has to be first
+$(obj)/vdso64.so.dbg: $(src)/vdso64.lds $(obj-vdso64)
+ $(call if_changed,vdso64ld)
+
+# 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-vdso64): %.o: %.S
+ $(call if_changed_dep,vdso64as)
+
+# actual build commands
+quiet_cmd_vdso64ld = VDSO64L $@
+ cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@
+quiet_cmd_vdso64as = VDSO64A $@
+ cmd_vdso64as = $(CC) $(a_flags) -c -o $@ $<
+
+# install commands for the unstripped file
+quiet_cmd_vdso_install = INSTALL $@
+ cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
+
+vdso64.so: $(obj)/vdso64.so.dbg
+ @mkdir -p $(MODLIB)/vdso
+ $(call cmd,vdso_install)
+
+vdso_install: vdso64.so
diff --git a/arch/s390/kernel/vdso64/clock_getres.S b/arch/s390/kernel/vdso64/clock_getres.S
new file mode 100644
index 00000000..176e1f75
--- /dev/null
+++ b/arch/s390/kernel/vdso64/clock_getres.S
@@ -0,0 +1,44 @@
+/*
+ * Userland implementation of clock_getres() for 64 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_clock_getres
+ .type __kernel_clock_getres,@function
+__kernel_clock_getres:
+ .cfi_startproc
+ cghi %r2,__CLOCK_REALTIME
+ je 0f
+ cghi %r2,__CLOCK_MONOTONIC
+ je 0f
+ cghi %r2,-2 /* CLOCK_THREAD_CPUTIME_ID for this thread */
+ jne 2f
+ larl %r5,_vdso_data
+ icm %r0,15,__LC_ECTG_OK(%r5)
+ jz 2f
+0: ltgr %r3,%r3
+ jz 1f /* res == NULL */
+ larl %r1,3f
+ lg %r0,0(%r1)
+ xc 0(8,%r3),0(%r3) /* set tp->tv_sec to zero */
+ stg %r0,8(%r3) /* store tp->tv_usec */
+1: lghi %r2,0
+ br %r14
+2: lghi %r1,__NR_clock_getres /* fallback to svc */
+ svc 0
+ br %r14
+3: .quad __CLOCK_REALTIME_RES
+ .cfi_endproc
+ .size __kernel_clock_getres,.-__kernel_clock_getres
diff --git a/arch/s390/kernel/vdso64/clock_gettime.S b/arch/s390/kernel/vdso64/clock_gettime.S
new file mode 100644
index 00000000..d46c95ed
--- /dev/null
+++ b/arch/s390/kernel/vdso64/clock_gettime.S
@@ -0,0 +1,125 @@
+/*
+ * Userland implementation of clock_gettime() for 64 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_clock_gettime
+ .type __kernel_clock_gettime,@function
+__kernel_clock_gettime:
+ .cfi_startproc
+ larl %r5,_vdso_data
+ cghi %r2,__CLOCK_REALTIME
+ je 4f
+ cghi %r2,-2 /* CLOCK_THREAD_CPUTIME_ID for this thread */
+ je 9f
+ cghi %r2,__CLOCK_MONOTONIC
+ jne 12f
+
+ /* CLOCK_MONOTONIC */
+ ltgr %r3,%r3
+ jz 3f /* tp == NULL */
+0: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */
+ tmll %r4,0x0001 /* pending update ? loop */
+ jnz 0b
+ stck 48(%r15) /* Store TOD clock */
+ lg %r1,48(%r15)
+ sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
+ msgf %r1,__VDSO_NTP_MULT(%r5) /* * NTP adjustment */
+ srlg %r1,%r1,12 /* cyc2ns(clock,cycle_delta) */
+ alg %r1,__VDSO_XTIME_NSEC(%r5) /* + xtime */
+ lg %r0,__VDSO_XTIME_SEC(%r5)
+ alg %r1,__VDSO_WTOM_NSEC(%r5) /* + wall_to_monotonic */
+ alg %r0,__VDSO_WTOM_SEC(%r5)
+ clg %r4,__VDSO_UPD_COUNT(%r5) /* check update counter */
+ jne 0b
+ larl %r5,13f
+1: clg %r1,0(%r5)
+ jl 2f
+ slg %r1,0(%r5)
+ aghi %r0,1
+ j 1b
+2: stg %r0,0(%r3) /* store tp->tv_sec */
+ stg %r1,8(%r3) /* store tp->tv_nsec */
+3: lghi %r2,0
+ br %r14
+
+ /* CLOCK_REALTIME */
+4: ltr %r3,%r3 /* tp == NULL */
+ jz 8f
+5: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */
+ tmll %r4,0x0001 /* pending update ? loop */
+ jnz 5b
+ stck 48(%r15) /* Store TOD clock */
+ lg %r1,48(%r15)
+ sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
+ msgf %r1,__VDSO_NTP_MULT(%r5) /* * NTP adjustment */
+ srlg %r1,%r1,12 /* cyc2ns(clock,cycle_delta) */
+ alg %r1,__VDSO_XTIME_NSEC(%r5) /* + xtime */
+ lg %r0,__VDSO_XTIME_SEC(%r5)
+ clg %r4,__VDSO_UPD_COUNT(%r5) /* check update counter */
+ jne 5b
+ larl %r5,13f
+6: clg %r1,0(%r5)
+ jl 7f
+ slg %r1,0(%r5)
+ aghi %r0,1
+ j 6b
+7: stg %r0,0(%r3) /* store tp->tv_sec */
+ stg %r1,8(%r3) /* store tp->tv_nsec */
+8: lghi %r2,0
+ br %r14
+
+ /* CLOCK_THREAD_CPUTIME_ID for this thread */
+9: icm %r0,15,__VDSO_ECTG_OK(%r5)
+ jz 12f
+ ear %r2,%a4
+ llilh %r4,0x0100
+ sar %a4,%r4
+ lghi %r4,0
+ epsw %r5,0
+ sacf 512 /* Magic ectg instruction */
+ .insn ssf,0xc80100000000,__VDSO_ECTG_BASE(4),__VDSO_ECTG_USER(4),4
+ tml %r5,0x4000
+ jo 11f
+ tml %r5,0x8000
+ jno 10f
+ sacf 256
+ j 11f
+10: sacf 0
+11: sar %a4,%r2
+ algr %r1,%r0 /* r1 = cputime as TOD value */
+ mghi %r1,1000 /* convert to nanoseconds */
+ srlg %r1,%r1,12 /* r1 = cputime in nanosec */
+ lgr %r4,%r1
+ larl %r5,13f
+ srlg %r1,%r1,9 /* divide by 1000000000 */
+ mlg %r0,8(%r5)
+ srlg %r0,%r0,11 /* r0 = tv_sec */
+ stg %r0,0(%r3)
+ msg %r0,0(%r5) /* calculate tv_nsec */
+ slgr %r4,%r0 /* r4 = tv_nsec */
+ stg %r4,8(%r3)
+ lghi %r2,0
+ br %r14
+
+ /* Fallback to system call */
+12: lghi %r1,__NR_clock_gettime
+ svc 0
+ br %r14
+
+13: .quad 1000000000
+14: .quad 19342813113834067
+ .cfi_endproc
+ .size __kernel_clock_gettime,.-__kernel_clock_gettime
diff --git a/arch/s390/kernel/vdso64/gettimeofday.S b/arch/s390/kernel/vdso64/gettimeofday.S
new file mode 100644
index 00000000..36ee6747
--- /dev/null
+++ b/arch/s390/kernel/vdso64/gettimeofday.S
@@ -0,0 +1,56 @@
+/*
+ * Userland implementation of gettimeofday() for 64 bits processes in a
+ * s390 kernel for use in the vDSO
+ *
+ * Copyright IBM Corp. 2008
+ * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (version 2 only)
+ * as published by the Free Software Foundation.
+ */
+#include <asm/vdso.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+ .text
+ .align 4
+ .globl __kernel_gettimeofday
+ .type __kernel_gettimeofday,@function
+__kernel_gettimeofday:
+ .cfi_startproc
+ larl %r5,_vdso_data
+0: ltgr %r3,%r3 /* check if tz is NULL */
+ je 1f
+ mvc 0(8,%r3),__VDSO_TIMEZONE(%r5)
+1: ltgr %r2,%r2 /* check if tv is NULL */
+ je 4f
+ lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */
+ tmll %r4,0x0001 /* pending update ? loop */
+ jnz 0b
+ stck 48(%r15) /* Store TOD clock */
+ lg %r1,48(%r15)
+ sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */
+ msgf %r1,__VDSO_NTP_MULT(%r5) /* * NTP adjustment */
+ srlg %r1,%r1,12 /* cyc2ns(clock,cycle_delta) */
+ alg %r1,__VDSO_XTIME_NSEC(%r5) /* + xtime.tv_nsec */
+ lg %r0,__VDSO_XTIME_SEC(%r5) /* xtime.tv_sec */
+ clg %r4,__VDSO_UPD_COUNT(%r5) /* check update counter */
+ jne 0b
+ larl %r5,5f
+2: clg %r1,0(%r5)
+ jl 3f
+ slg %r1,0(%r5)
+ aghi %r0,1
+ j 2b
+3: stg %r0,0(%r2) /* store tv->tv_sec */
+ slgr %r0,%r0 /* tv_nsec -> tv_usec */
+ ml %r0,8(%r5)
+ srlg %r0,%r0,6
+ stg %r0,8(%r2) /* store tv->tv_usec */
+4: lghi %r2,0
+ br %r14
+5: .quad 1000000000
+ .long 274877907
+ .cfi_endproc
+ .size __kernel_gettimeofday,.-__kernel_gettimeofday
diff --git a/arch/s390/kernel/vdso64/note.S b/arch/s390/kernel/vdso64/note.S
new file mode 100644
index 00000000..79a071e4
--- /dev/null
+++ b/arch/s390/kernel/vdso64/note.S
@@ -0,0 +1,12 @@
+/*
+ * 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
diff --git a/arch/s390/kernel/vdso64/vdso64.lds.S b/arch/s390/kernel/vdso64/vdso64.lds.S
new file mode 100644
index 00000000..9f5979d1
--- /dev/null
+++ b/arch/s390/kernel/vdso64/vdso64.lds.S
@@ -0,0 +1,138 @@
+/*
+ * This is the infamous ld script for the 64 bits vdso
+ * library
+ */
+#include <asm/vdso.h>
+
+OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
+OUTPUT_ARCH(s390:64-bit)
+ENTRY(_start)
+
+SECTIONS
+{
+ . = VDSO64_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(4096);
+ 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;
+
+ local: *;
+ };
+}
diff --git a/arch/s390/kernel/vdso64/vdso64_wrapper.S b/arch/s390/kernel/vdso64/vdso64_wrapper.S
new file mode 100644
index 00000000..c245842b
--- /dev/null
+++ b/arch/s390/kernel/vdso64/vdso64_wrapper.S
@@ -0,0 +1,14 @@
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <asm/page.h>
+
+ __PAGE_ALIGNED_DATA
+
+ .globl vdso64_start, vdso64_end
+ .balign PAGE_SIZE
+vdso64_start:
+ .incbin "arch/s390/kernel/vdso64/vdso64.so"
+ .balign PAGE_SIZE
+vdso64_end:
+
+ .previous
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
new file mode 100644
index 00000000..21109c63
--- /dev/null
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -0,0 +1,96 @@
+/* ld script to make s390 Linux kernel
+ * Written by Martin Schwidefsky (schwidefsky@de.ibm.com)
+ */
+
+#include <asm/thread_info.h>
+#include <asm/page.h>
+#include <asm-generic/vmlinux.lds.h>
+
+#ifndef CONFIG_64BIT
+OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390")
+OUTPUT_ARCH(s390)
+ENTRY(startup)
+jiffies = jiffies_64 + 4;
+#else
+OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
+OUTPUT_ARCH(s390:64-bit)
+ENTRY(startup)
+jiffies = jiffies_64;
+#endif
+
+PHDRS {
+ text PT_LOAD FLAGS(5); /* R_E */
+ data PT_LOAD FLAGS(7); /* RWE */
+ note PT_NOTE FLAGS(0); /* ___ */
+}
+
+SECTIONS
+{
+ . = 0x00000000;
+ .text : {
+ _text = .; /* Text and read-only data */
+ HEAD_TEXT
+ TEXT_TEXT
+ SCHED_TEXT
+ LOCK_TEXT
+ KPROBES_TEXT
+ IRQENTRY_TEXT
+ *(.fixup)
+ *(.gnu.warning)
+ } :text = 0x0700
+
+ _etext = .; /* End of text section */
+
+ NOTES :text :note
+
+ .dummy : { *(.dummy) } :data
+
+ RODATA
+
+#ifdef CONFIG_SHARED_KERNEL
+ . = ALIGN(0x100000); /* VM shared segments are 1MB aligned */
+#endif
+
+ . = ALIGN(PAGE_SIZE);
+ _eshared = .; /* End of shareable data */
+ _sdata = .; /* Start of data section */
+
+ EXCEPTION_TABLE(16) :data
+
+ RW_DATA_SECTION(0x100, PAGE_SIZE, THREAD_SIZE)
+
+ _edata = .; /* End of data section */
+
+ /* will be freed after init */
+ . = ALIGN(PAGE_SIZE); /* Init code and data */
+ __init_begin = .;
+
+ INIT_TEXT_SECTION(PAGE_SIZE)
+
+ /*
+ * .exit.text is discarded at runtime, not link time,
+ * to deal with references from __bug_table
+ */
+ .exit.text : {
+ EXIT_TEXT
+ }
+
+ /* early.c uses stsi, which requires page aligned data. */
+ . = ALIGN(PAGE_SIZE);
+ INIT_DATA_SECTION(0x100)
+
+ PERCPU_SECTION(0x100)
+ . = ALIGN(PAGE_SIZE);
+ __init_end = .; /* freed after init ends here */
+
+ BSS_SECTION(0, 2, 0)
+
+ _end = . ;
+
+ /* Debugging sections. */
+ STABS_DEBUG
+ DWARF_DEBUG
+
+ /* Sections to be discarded */
+ DISCARDS
+}
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
new file mode 100644
index 00000000..39ebff50
--- /dev/null
+++ b/arch/s390/kernel/vtime.c
@@ -0,0 +1,504 @@
+/*
+ * arch/s390/kernel/vtime.c
+ * Virtual cpu timer based timer functions.
+ *
+ * S390 version
+ * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Jan Glauber <jan.glauber@de.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+#include <linux/timex.h>
+#include <linux/notifier.h>
+#include <linux/kernel_stat.h>
+#include <linux/rcupdate.h>
+#include <linux/posix-timers.h>
+#include <linux/cpu.h>
+#include <linux/kprobes.h>
+
+#include <asm/timer.h>
+#include <asm/irq_regs.h>
+#include <asm/cputime.h>
+#include <asm/irq.h>
+#include "entry.h"
+
+static DEFINE_PER_CPU(struct vtimer_queue, virt_cpu_timer);
+
+DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
+
+static inline __u64 get_vtimer(void)
+{
+ __u64 timer;
+
+ asm volatile("STPT %0" : "=m" (timer));
+ return timer;
+}
+
+static inline void set_vtimer(__u64 expires)
+{
+ __u64 timer;
+
+ asm volatile (" STPT %0\n" /* Store current cpu timer value */
+ " SPT %1" /* Set new value immediately afterwards */
+ : "=m" (timer) : "m" (expires) );
+ S390_lowcore.system_timer += S390_lowcore.last_update_timer - timer;
+ S390_lowcore.last_update_timer = expires;
+}
+
+/*
+ * Update process times based on virtual cpu times stored by entry.S
+ * to the lowcore fields user_timer, system_timer & steal_clock.
+ */
+static void do_account_vtime(struct task_struct *tsk, int hardirq_offset)
+{
+ struct thread_info *ti = task_thread_info(tsk);
+ __u64 timer, clock, user, system, steal;
+
+ timer = S390_lowcore.last_update_timer;
+ clock = S390_lowcore.last_update_clock;
+ asm volatile (" STPT %0\n" /* Store current cpu timer value */
+ " STCK %1" /* Store current tod clock value */
+ : "=m" (S390_lowcore.last_update_timer),
+ "=m" (S390_lowcore.last_update_clock) );
+ S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer;
+ S390_lowcore.steal_timer += S390_lowcore.last_update_clock - clock;
+
+ user = S390_lowcore.user_timer - ti->user_timer;
+ S390_lowcore.steal_timer -= user;
+ ti->user_timer = S390_lowcore.user_timer;
+ account_user_time(tsk, user, user);
+
+ system = S390_lowcore.system_timer - ti->system_timer;
+ S390_lowcore.steal_timer -= system;
+ ti->system_timer = S390_lowcore.system_timer;
+ account_system_time(tsk, hardirq_offset, system, system);
+
+ steal = S390_lowcore.steal_timer;
+ if ((s64) steal > 0) {
+ S390_lowcore.steal_timer = 0;
+ account_steal_time(steal);
+ }
+}
+
+void account_vtime(struct task_struct *prev, struct task_struct *next)
+{
+ struct thread_info *ti;
+
+ do_account_vtime(prev, 0);
+ ti = task_thread_info(prev);
+ ti->user_timer = S390_lowcore.user_timer;
+ ti->system_timer = S390_lowcore.system_timer;
+ ti = task_thread_info(next);
+ S390_lowcore.user_timer = ti->user_timer;
+ S390_lowcore.system_timer = ti->system_timer;
+}
+
+void account_process_tick(struct task_struct *tsk, int user_tick)
+{
+ do_account_vtime(tsk, HARDIRQ_OFFSET);
+}
+
+/*
+ * Update process times based on virtual cpu times stored by entry.S
+ * to the lowcore fields user_timer, system_timer & steal_clock.
+ */
+void account_system_vtime(struct task_struct *tsk)
+{
+ struct thread_info *ti = task_thread_info(tsk);
+ __u64 timer, system;
+
+ timer = S390_lowcore.last_update_timer;
+ S390_lowcore.last_update_timer = get_vtimer();
+ S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer;
+
+ system = S390_lowcore.system_timer - ti->system_timer;
+ S390_lowcore.steal_timer -= system;
+ ti->system_timer = S390_lowcore.system_timer;
+ account_system_time(tsk, 0, system, system);
+}
+EXPORT_SYMBOL_GPL(account_system_vtime);
+
+void __kprobes vtime_stop_cpu(void)
+{
+ struct s390_idle_data *idle = &__get_cpu_var(s390_idle);
+ struct vtimer_queue *vq = &__get_cpu_var(virt_cpu_timer);
+ unsigned long long idle_time;
+ unsigned long psw_mask;
+
+ trace_hardirqs_on();
+ /* Don't trace preempt off for idle. */
+ stop_critical_timings();
+
+ /* Wait for external, I/O or machine check interrupt. */
+ psw_mask = psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_DAT |
+ PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
+ idle->nohz_delay = 0;
+
+ /* Call the assembler magic in entry.S */
+ psw_idle(idle, vq, psw_mask, !list_empty(&vq->list));
+
+ /* Reenable preemption tracer. */
+ start_critical_timings();
+
+ /* Account time spent with enabled wait psw loaded as idle time. */
+ idle->sequence++;
+ smp_wmb();
+ idle_time = idle->idle_exit - idle->idle_enter;
+ idle->idle_time += idle_time;
+ idle->idle_enter = idle->idle_exit = 0ULL;
+ idle->idle_count++;
+ account_idle_time(idle_time);
+ smp_wmb();
+ idle->sequence++;
+}
+
+cputime64_t s390_get_idle_time(int cpu)
+{
+ struct s390_idle_data *idle = &per_cpu(s390_idle, cpu);
+ unsigned long long now, idle_enter, idle_exit;
+ unsigned int sequence;
+
+ do {
+ now = get_clock();
+ sequence = ACCESS_ONCE(idle->sequence);
+ idle_enter = ACCESS_ONCE(idle->idle_enter);
+ idle_exit = ACCESS_ONCE(idle->idle_exit);
+ } while ((sequence & 1) || (idle->sequence != sequence));
+ return idle_enter ? ((idle_exit ? : now) - idle_enter) : 0;
+}
+
+/*
+ * Sorted add to a list. List is linear searched until first bigger
+ * element is found.
+ */
+static void list_add_sorted(struct vtimer_list *timer, struct list_head *head)
+{
+ struct vtimer_list *event;
+
+ list_for_each_entry(event, head, entry) {
+ if (event->expires > timer->expires) {
+ list_add_tail(&timer->entry, &event->entry);
+ return;
+ }
+ }
+ list_add_tail(&timer->entry, head);
+}
+
+/*
+ * Do the callback functions of expired vtimer events.
+ * Called from within the interrupt handler.
+ */
+static void do_callbacks(struct list_head *cb_list)
+{
+ struct vtimer_queue *vq;
+ struct vtimer_list *event, *tmp;
+
+ if (list_empty(cb_list))
+ return;
+
+ vq = &__get_cpu_var(virt_cpu_timer);
+
+ list_for_each_entry_safe(event, tmp, cb_list, entry) {
+ list_del_init(&event->entry);
+ (event->function)(event->data);
+ if (event->interval) {
+ /* Recharge interval timer */
+ event->expires = event->interval + vq->elapsed;
+ spin_lock(&vq->lock);
+ list_add_sorted(event, &vq->list);
+ spin_unlock(&vq->lock);
+ }
+ }
+}
+
+/*
+ * Handler for the virtual CPU timer.
+ */
+static void do_cpu_timer_interrupt(struct ext_code ext_code,
+ unsigned int param32, unsigned long param64)
+{
+ struct vtimer_queue *vq;
+ struct vtimer_list *event, *tmp;
+ struct list_head cb_list; /* the callback queue */
+ __u64 elapsed, next;
+
+ kstat_cpu(smp_processor_id()).irqs[EXTINT_TMR]++;
+ INIT_LIST_HEAD(&cb_list);
+ vq = &__get_cpu_var(virt_cpu_timer);
+
+ /* walk timer list, fire all expired events */
+ spin_lock(&vq->lock);
+
+ elapsed = vq->elapsed + (vq->timer - S390_lowcore.async_enter_timer);
+ BUG_ON((s64) elapsed < 0);
+ vq->elapsed = 0;
+ list_for_each_entry_safe(event, tmp, &vq->list, entry) {
+ if (event->expires < elapsed)
+ /* move expired timer to the callback queue */
+ list_move_tail(&event->entry, &cb_list);
+ else
+ event->expires -= elapsed;
+ }
+ spin_unlock(&vq->lock);
+
+ do_callbacks(&cb_list);
+
+ /* next event is first in list */
+ next = VTIMER_MAX_SLICE;
+ spin_lock(&vq->lock);
+ if (!list_empty(&vq->list)) {
+ event = list_first_entry(&vq->list, struct vtimer_list, entry);
+ next = event->expires;
+ }
+ spin_unlock(&vq->lock);
+ /*
+ * To improve precision add the time spent by the
+ * interrupt handler to the elapsed time.
+ * Note: CPU timer counts down and we got an interrupt,
+ * the current content is negative
+ */
+ elapsed = S390_lowcore.async_enter_timer - get_vtimer();
+ set_vtimer(next - elapsed);
+ vq->timer = next - elapsed;
+ vq->elapsed = elapsed;
+}
+
+void init_virt_timer(struct vtimer_list *timer)
+{
+ timer->function = NULL;
+ INIT_LIST_HEAD(&timer->entry);
+}
+EXPORT_SYMBOL(init_virt_timer);
+
+static inline int vtimer_pending(struct vtimer_list *timer)
+{
+ return (!list_empty(&timer->entry));
+}
+
+/*
+ * this function should only run on the specified CPU
+ */
+static void internal_add_vtimer(struct vtimer_list *timer)
+{
+ struct vtimer_queue *vq;
+ unsigned long flags;
+ __u64 left, expires;
+
+ vq = &per_cpu(virt_cpu_timer, timer->cpu);
+ spin_lock_irqsave(&vq->lock, flags);
+
+ BUG_ON(timer->cpu != smp_processor_id());
+
+ if (list_empty(&vq->list)) {
+ /* First timer on this cpu, just program it. */
+ list_add(&timer->entry, &vq->list);
+ set_vtimer(timer->expires);
+ vq->timer = timer->expires;
+ vq->elapsed = 0;
+ } else {
+ /* Check progress of old timers. */
+ expires = timer->expires;
+ left = get_vtimer();
+ if (likely((s64) expires < (s64) left)) {
+ /* The new timer expires before the current timer. */
+ set_vtimer(expires);
+ vq->elapsed += vq->timer - left;
+ vq->timer = expires;
+ } else {
+ vq->elapsed += vq->timer - left;
+ vq->timer = left;
+ }
+ /* Insert new timer into per cpu list. */
+ timer->expires += vq->elapsed;
+ list_add_sorted(timer, &vq->list);
+ }
+
+ spin_unlock_irqrestore(&vq->lock, flags);
+ /* release CPU acquired in prepare_vtimer or mod_virt_timer() */
+ put_cpu();
+}
+
+static inline void prepare_vtimer(struct vtimer_list *timer)
+{
+ BUG_ON(!timer->function);
+ BUG_ON(!timer->expires || timer->expires > VTIMER_MAX_SLICE);
+ BUG_ON(vtimer_pending(timer));
+ timer->cpu = get_cpu();
+}
+
+/*
+ * add_virt_timer - add an oneshot virtual CPU timer
+ */
+void add_virt_timer(void *new)
+{
+ struct vtimer_list *timer;
+
+ timer = (struct vtimer_list *)new;
+ prepare_vtimer(timer);
+ timer->interval = 0;
+ internal_add_vtimer(timer);
+}
+EXPORT_SYMBOL(add_virt_timer);
+
+/*
+ * add_virt_timer_int - add an interval virtual CPU timer
+ */
+void add_virt_timer_periodic(void *new)
+{
+ struct vtimer_list *timer;
+
+ timer = (struct vtimer_list *)new;
+ prepare_vtimer(timer);
+ timer->interval = timer->expires;
+ internal_add_vtimer(timer);
+}
+EXPORT_SYMBOL(add_virt_timer_periodic);
+
+static int __mod_vtimer(struct vtimer_list *timer, __u64 expires, int periodic)
+{
+ struct vtimer_queue *vq;
+ unsigned long flags;
+ int cpu;
+
+ BUG_ON(!timer->function);
+ BUG_ON(!expires || expires > VTIMER_MAX_SLICE);
+
+ if (timer->expires == expires && vtimer_pending(timer))
+ return 1;
+
+ cpu = get_cpu();
+ vq = &per_cpu(virt_cpu_timer, cpu);
+
+ /* disable interrupts before test if timer is pending */
+ spin_lock_irqsave(&vq->lock, flags);
+
+ /* if timer isn't pending add it on the current CPU */
+ if (!vtimer_pending(timer)) {
+ spin_unlock_irqrestore(&vq->lock, flags);
+
+ if (periodic)
+ timer->interval = expires;
+ else
+ timer->interval = 0;
+ timer->expires = expires;
+ timer->cpu = cpu;
+ internal_add_vtimer(timer);
+ return 0;
+ }
+
+ /* check if we run on the right CPU */
+ BUG_ON(timer->cpu != cpu);
+
+ list_del_init(&timer->entry);
+ timer->expires = expires;
+ if (periodic)
+ timer->interval = expires;
+
+ /* the timer can't expire anymore so we can release the lock */
+ spin_unlock_irqrestore(&vq->lock, flags);
+ internal_add_vtimer(timer);
+ return 1;
+}
+
+/*
+ * If we change a pending timer the function must be called on the CPU
+ * where the timer is running on.
+ *
+ * returns whether it has modified a pending timer (1) or not (0)
+ */
+int mod_virt_timer(struct vtimer_list *timer, __u64 expires)
+{
+ return __mod_vtimer(timer, expires, 0);
+}
+EXPORT_SYMBOL(mod_virt_timer);
+
+/*
+ * If we change a pending timer the function must be called on the CPU
+ * where the timer is running on.
+ *
+ * returns whether it has modified a pending timer (1) or not (0)
+ */
+int mod_virt_timer_periodic(struct vtimer_list *timer, __u64 expires)
+{
+ return __mod_vtimer(timer, expires, 1);
+}
+EXPORT_SYMBOL(mod_virt_timer_periodic);
+
+/*
+ * delete a virtual timer
+ *
+ * returns whether the deleted timer was pending (1) or not (0)
+ */
+int del_virt_timer(struct vtimer_list *timer)
+{
+ unsigned long flags;
+ struct vtimer_queue *vq;
+
+ /* check if timer is pending */
+ if (!vtimer_pending(timer))
+ return 0;
+
+ vq = &per_cpu(virt_cpu_timer, timer->cpu);
+ spin_lock_irqsave(&vq->lock, flags);
+
+ /* we don't interrupt a running timer, just let it expire! */
+ list_del_init(&timer->entry);
+
+ spin_unlock_irqrestore(&vq->lock, flags);
+ return 1;
+}
+EXPORT_SYMBOL(del_virt_timer);
+
+/*
+ * Start the virtual CPU timer on the current CPU.
+ */
+void init_cpu_vtimer(void)
+{
+ struct vtimer_queue *vq;
+
+ /* initialize per cpu vtimer structure */
+ vq = &__get_cpu_var(virt_cpu_timer);
+ INIT_LIST_HEAD(&vq->list);
+ spin_lock_init(&vq->lock);
+
+ /* enable cpu timer interrupts */
+ __ctl_set_bit(0,10);
+
+ /* set initial cpu timer */
+ set_vtimer(0x7fffffffffffffffULL);
+}
+
+static int __cpuinit s390_nohz_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ struct s390_idle_data *idle;
+ long cpu = (long) hcpu;
+
+ idle = &per_cpu(s390_idle, cpu);
+ switch (action) {
+ case CPU_DYING:
+ case CPU_DYING_FROZEN:
+ idle->nohz_delay = 0;
+ default:
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+void __init vtime_init(void)
+{
+ /* request the cpu timer external interrupt */
+ if (register_external_interrupt(0x1005, do_cpu_timer_interrupt))
+ panic("Couldn't request external interrupt 0x1005");
+
+ /* Enable cpu timer interrupts on the boot cpu. */
+ init_cpu_vtimer();
+ cpu_notifier(s390_nohz_notify, 0);
+}
+