--- /dev/null
+From: Gerald Schaefer <geraldsc@de.ibm.com>
+Subject: kernel: Fix user copy functions (pagetable walk) with KERNEL_DS.
+References: bnc#466462
+
+Symptom: Kernel OOPS / system hang on user program core dump on pre z9
+ hardware.
+Problem: Passing incorrect addresses to user copy functions is not handled
+ correctly when address spaces are switched and KERNEL_DS is set.
+Solution: Verify addresses by handling KERNEL_DS case in the same way as
+ USER_DS (pagetable walk). Also disable switch_amode by default
+ because pagetable walk has negative performance impact.
+
+Acked-by: John Jolly <jjolly@suse.de>
+---
+ arch/s390/kernel/setup.c | 4 ----
+ arch/s390/lib/uaccess_pt.c | 32 +++++++-------------------------
+ arch/s390/mm/pgtable.c | 4 ++++
+ 3 files changed, 11 insertions(+), 29 deletions(-)
+
+Index: linux-sles11/arch/s390/kernel/setup.c
+===================================================================
+--- linux-sles11.orig/arch/s390/kernel/setup.c
++++ linux-sles11/arch/s390/kernel/setup.c
+@@ -285,11 +285,7 @@ static int __init early_parse_mem(char *
+ early_param("mem", early_parse_mem);
+
+ #ifdef CONFIG_S390_SWITCH_AMODE
+-#ifdef CONFIG_PGSTE
+-unsigned int switch_amode = 1;
+-#else
+ unsigned int switch_amode = 0;
+-#endif
+ EXPORT_SYMBOL_GPL(switch_amode);
+
+ static int set_amode_and_uaccess(unsigned long user_amode,
+Index: linux-sles11/arch/s390/mm/pgtable.c
+===================================================================
+--- linux-sles11.orig/arch/s390/mm/pgtable.c
++++ linux-sles11/arch/s390/mm/pgtable.c
+@@ -256,6 +256,10 @@ int s390_enable_sie(void)
+ struct task_struct *tsk = current;
+ struct mm_struct *mm, *old_mm;
+
++ /* Do we have switched amode? If no, we cannot do sie */
++ if (!switch_amode)
++ return -EINVAL;
++
+ /* Do we have pgstes? if yes, we are done */
+ if (tsk->mm->context.pgstes)
+ return 0;
+Index: linux-sles11/arch/s390/lib/uaccess_pt.c
+===================================================================
+--- linux-sles11.orig/arch/s390/lib/uaccess_pt.c
++++ linux-sles11/arch/s390/lib/uaccess_pt.c
+@@ -43,8 +43,9 @@ static int __handle_fault(struct mm_stru
+ int ret = -EFAULT;
+ int fault;
+
+- if (in_atomic())
++ if (in_atomic() || segment_eq(get_fs(), KERNEL_DS))
+ return ret;
++
+ down_read(&mm->mmap_sem);
+ vma = find_vma(mm, address);
+ if (unlikely(!vma))
+@@ -109,6 +110,8 @@ static size_t __user_copy_pt(unsigned lo
+ pte_t *pte;
+ void *from, *to;
+
++ if (segment_eq(get_fs(), KERNEL_DS))
++ mm = &init_mm;
+ done = 0;
+ retry:
+ spin_lock(&mm->page_table_lock);
+@@ -182,10 +185,6 @@ size_t copy_from_user_pt(size_t n, const
+ {
+ size_t rc;
+
+- if (segment_eq(get_fs(), KERNEL_DS)) {
+- memcpy(to, (void __kernel __force *) from, n);
+- return 0;
+- }
+ rc = __user_copy_pt((unsigned long) from, to, n, 0);
+ if (unlikely(rc))
+ memset(to + n - rc, 0, rc);
+@@ -194,10 +193,6 @@ size_t copy_from_user_pt(size_t n, const
+
+ size_t copy_to_user_pt(size_t n, void __user *to, const void *from)
+ {
+- if (segment_eq(get_fs(), KERNEL_DS)) {
+- memcpy((void __kernel __force *) to, from, n);
+- return 0;
+- }
+ return __user_copy_pt((unsigned long) to, (void *) from, n, 1);
+ }
+
+@@ -205,10 +200,6 @@ static size_t clear_user_pt(size_t n, vo
+ {
+ long done, size, ret;
+
+- if (segment_eq(get_fs(), KERNEL_DS)) {
+- memset((void __kernel __force *) to, 0, n);
+- return 0;
+- }
+ done = 0;
+ do {
+ if (n - done > PAGE_SIZE)
+@@ -234,7 +225,7 @@ static size_t strnlen_user_pt(size_t cou
+ size_t len_str;
+
+ if (segment_eq(get_fs(), KERNEL_DS))
+- return strnlen((const char __kernel __force *) src, count) + 1;
++ mm = &init_mm;
+ done = 0;
+ retry:
+ spin_lock(&mm->page_table_lock);
+@@ -276,13 +267,6 @@ static size_t strncpy_from_user_pt(size_
+ return -EFAULT;
+ if (n > count)
+ n = count;
+- if (segment_eq(get_fs(), KERNEL_DS)) {
+- memcpy(dst, (const char __kernel __force *) src, n);
+- if (dst[n-1] == '\0')
+- return n-1;
+- else
+- return n;
+- }
+ if (__user_copy_pt((unsigned long) src, dst, n, 0))
+ return -EFAULT;
+ if (dst[n-1] == '\0')
+@@ -302,10 +286,8 @@ static size_t copy_in_user_pt(size_t n,
+ pte_t *pte_from, *pte_to;
+ int write_user;
+
+- if (segment_eq(get_fs(), KERNEL_DS)) {
+- memcpy((void __force *) to, (void __force *) from, n);
+- return 0;
+- }
++ if (segment_eq(get_fs(), KERNEL_DS))
++ mm = &init_mm;
+ done = 0;
+ retry:
+ spin_lock(&mm->page_table_lock);