From 92fe31329cb3a2b02f1c7616965872d6a34bcf08 Mon Sep 17 00:00:00 2001 From: Michael Holzheu Date: Wed, 24 Mar 2010 11:49:50 +0100 Subject: [S390] zcore: CPU registers are not saved under LPAR To save the registers for all CPUs a sigp "store status" is done that stores the registers to address absolute zero. To access storage at absolute zero, normally the address of the prefix register of the accessing CPU has to be used. This does not work when large pages are active (currently only under LPAR). In order to fix that problem, instead of memcpy memcpy_real is used, which switches to real mode where prefixing works. Signed-off-by: Michael Holzheu Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/system.h | 1 + arch/s390/kernel/smp.c | 6 +++--- arch/s390/mm/maccess.c | 26 ++++++++++++++++++++++++++ drivers/s390/char/zcore.c | 31 ++----------------------------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/arch/s390/include/asm/system.h b/arch/s390/include/asm/system.h index 67ee6c3c6bb..12be42baa05 100644 --- a/arch/s390/include/asm/system.h +++ b/arch/s390/include/asm/system.h @@ -110,6 +110,7 @@ extern void pfault_fini(void); #endif /* CONFIG_PFAULT */ extern void cmma_init(void); +extern int memcpy_real(void *, void *, size_t); #define finish_arch_switch(prev) do { \ set_fs(current->thread.mm_segment); \ diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 29f65bce55e..d7d24fc3d6b 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -292,9 +292,9 @@ static void __init smp_get_save_area(unsigned int cpu, unsigned int phy_cpu) zfcpdump_save_areas[cpu] = kmalloc(sizeof(struct save_area), GFP_KERNEL); while (raw_sigp(phy_cpu, sigp_stop_and_store_status) == sigp_busy) cpu_relax(); - memcpy(zfcpdump_save_areas[cpu], - (void *)(unsigned long) store_prefix() + SAVE_AREA_BASE, - sizeof(struct save_area)); + memcpy_real(zfcpdump_save_areas[cpu], + (void *)(unsigned long) store_prefix() + SAVE_AREA_BASE, + sizeof(struct save_area)); } struct save_area *zfcpdump_save_areas[NR_CPUS + 1]; diff --git a/arch/s390/mm/maccess.c b/arch/s390/mm/maccess.c index 81756271dc4..a8c2af8c650 100644 --- a/arch/s390/mm/maccess.c +++ b/arch/s390/mm/maccess.c @@ -59,3 +59,29 @@ long probe_kernel_write(void *dst, void *src, size_t size) } return copied < 0 ? -EFAULT : 0; } + +int memcpy_real(void *dest, void *src, size_t count) +{ + register unsigned long _dest asm("2") = (unsigned long) dest; + register unsigned long _len1 asm("3") = (unsigned long) count; + register unsigned long _src asm("4") = (unsigned long) src; + register unsigned long _len2 asm("5") = (unsigned long) count; + unsigned long flags; + int rc = -EFAULT; + + if (!count) + return 0; + flags = __raw_local_irq_stnsm(0xf8UL); + asm volatile ( + "0: mvcle %1,%2,0x0\n" + "1: jo 0b\n" + " lhi %0,0x0\n" + "2:\n" + EX_TABLE(1b,2b) + : "+d" (rc), "+d" (_dest), "+d" (_src), "+d" (_len1), + "+d" (_len2), "=m" (*((long *) dest)) + : "m" (*((long *) src)) + : "cc", "memory"); + __raw_local_irq_ssm(flags); + return rc; +} diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 3438658b66b..3166d85914f 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c @@ -141,33 +141,6 @@ static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count) return memcpy_hsa(dest, src, count, TO_KERNEL); } -static int memcpy_real(void *dest, unsigned long src, size_t count) -{ - unsigned long flags; - int rc = -EFAULT; - register unsigned long _dest asm("2") = (unsigned long) dest; - register unsigned long _len1 asm("3") = (unsigned long) count; - register unsigned long _src asm("4") = src; - register unsigned long _len2 asm("5") = (unsigned long) count; - - if (count == 0) - return 0; - flags = __raw_local_irq_stnsm(0xf8UL); /* switch to real mode */ - asm volatile ( - "0: mvcle %1,%2,0x0\n" - "1: jo 0b\n" - " lhi %0,0x0\n" - "2:\n" - EX_TABLE(1b,2b) - : "+d" (rc), "+d" (_dest), "+d" (_src), "+d" (_len1), - "+d" (_len2), "=m" (*((long*)dest)) - : "m" (*((long*)src)) - : "cc", "memory"); - __raw_local_irq_ssm(flags); - - return rc; -} - static int memcpy_real_user(void __user *dest, unsigned long src, size_t count) { static char buf[4096]; @@ -175,7 +148,7 @@ static int memcpy_real_user(void __user *dest, unsigned long src, size_t count) while (offs < count) { size = min(sizeof(buf), count - offs); - if (memcpy_real(buf, src + offs, size)) + if (memcpy_real(buf, (void *) src + offs, size)) return -EFAULT; if (copy_to_user(dest + offs, buf, size)) return -EFAULT; @@ -663,7 +636,7 @@ static int __init zcore_reipl_init(void) if (ipib_info.ipib < ZFCPDUMP_HSA_SIZE) rc = memcpy_hsa_kernel(ipl_block, ipib_info.ipib, PAGE_SIZE); else - rc = memcpy_real(ipl_block, ipib_info.ipib, PAGE_SIZE); + rc = memcpy_real(ipl_block, (void *) ipib_info.ipib, PAGE_SIZE); if (rc) { free_page((unsigned long) ipl_block); return rc; -- cgit From a08786568a7e334d8a07a7bb8f4fafa3f7afb89c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 24 Mar 2010 11:49:51 +0100 Subject: [S390] smp: fix lowcore allocation The intermediate lowcore for CONFIG_SMP is allocated using a call to __alloc_bootmem() with a goal of 0. That however doesn't guarantee that the allocated piece of memory is below 2GB. Instead we should call __alloc_bootmem_low(). Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/setup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 77a63ae419f..ba363d99de4 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -401,7 +401,7 @@ setup_lowcore(void) * Setup lowcore for boot cpu */ BUILD_BUG_ON(sizeof(struct _lowcore) != LC_PAGES * 4096); - lc = __alloc_bootmem(LC_PAGES * PAGE_SIZE, LC_PAGES * PAGE_SIZE, 0); + lc = __alloc_bootmem_low(LC_PAGES * PAGE_SIZE, LC_PAGES * PAGE_SIZE, 0); lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; lc->restart_psw.addr = PSW_ADDR_AMODE | (unsigned long) restart_int_handler; @@ -433,7 +433,7 @@ setup_lowcore(void) #ifndef CONFIG_64BIT if (MACHINE_HAS_IEEE) { lc->extended_save_area_addr = (__u32) - __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0); + __alloc_bootmem_low(PAGE_SIZE, PAGE_SIZE, 0); /* enable extended save area */ __ctl_set_bit(14, 29); } -- cgit From 1bff779285f89cd0b62955c20cca70ada4074236 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Wed, 24 Mar 2010 11:49:52 +0100 Subject: [S390] system.h: Fix compile error for 1 and 2 byte cmpxchg commit 024914477e15ef8b17f271ec47f1bb8a589f0806 "memcg: move charges of anonymous swap" revealed that the 1 byte and 2 byte cmpxchg is currently broken: arch/s390/include/asm/system.h: Assembler messages: arch/s390/include/asm/system.h:241: Error: junk at end of line: `(%r5)' make[1]: *** [mm/page_cgroup.o] Error 1 make[1]: *** Waiting for unfinished jobs.... It turned out that commit 987bcdacb18a3adc2a48d85c9b005069c2f4dd7b ([S390] use inline assembly contraints available with gcc 3.3.3) broke the inline assembly. The or operands are now in constraint 3 and 4 instead of 2 and 3. Signed-off-by: Christian Borntraeger Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/system.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/s390/include/asm/system.h b/arch/s390/include/asm/system.h index 12be42baa05..1741c1556a4 100644 --- a/arch/s390/include/asm/system.h +++ b/arch/s390/include/asm/system.h @@ -219,8 +219,8 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) " l %0,%2\n" "0: nr %0,%5\n" " lr %1,%0\n" - " or %0,%2\n" - " or %1,%3\n" + " or %0,%3\n" + " or %1,%4\n" " cs %0,%1,%2\n" " jnl 1f\n" " xr %1,%0\n" @@ -240,8 +240,8 @@ __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size) " l %0,%2\n" "0: nr %0,%5\n" " lr %1,%0\n" - " or %0,%2\n" - " or %1,%3\n" + " or %0,%3\n" + " or %1,%4\n" " cs %0,%1,%2\n" " jnl 1f\n" " xr %1,%0\n" -- cgit From 4a31ba57cae853ce1ac00a22c0f5d80bd36685ed Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Wed, 24 Mar 2010 11:49:53 +0100 Subject: [S390] dasd: fix alignment of transport mode recovery TCW All TCWs need to be aligned on a 64 byte boundary or the I/O will be rejected. For recovery requests we create fresh TCWs, so we need to do the proper alignment here as well. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_3990_erp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 51224f76b98..b3736b8aad3 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -2287,7 +2287,8 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) if (cqr->cpmode == 1) { cplength = 0; - datasize = sizeof(struct tcw) + sizeof(struct tsb); + /* TCW needs to be 64 byte aligned, so leave enough room */ + datasize = 64 + sizeof(struct tcw) + sizeof(struct tsb); } else { cplength = 2; datasize = 0; @@ -2316,8 +2317,8 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) if (cqr->cpmode == 1) { /* make a shallow copy of the original tcw but set new tsb */ erp->cpmode = 1; - erp->cpaddr = erp->data; - tcw = erp->data; + erp->cpaddr = PTR_ALIGN(erp->data, 64); + tcw = erp->cpaddr; tsb = (struct tsb *) &tcw[1]; *tcw = *((struct tcw *)cqr->cpaddr); tcw->tsb = (long)tsb; -- cgit From b8fde7224d771ce55bfd67cb57d7c4c8f430972f Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 24 Mar 2010 11:49:54 +0100 Subject: [S390] dasd: check tsb validity Check tsb validity before the tcw_get_tsb function is called. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd_eckd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 01f4e7a34aa..0cb23311685 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -3155,11 +3155,11 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, tsb = NULL; sense = NULL; - if (irb->scsw.tm.tcw) + if (irb->scsw.tm.tcw && (irb->scsw.tm.fcxs == 0x01)) tsb = tcw_get_tsb( (struct tcw *)(unsigned long)irb->scsw.tm.tcw); - if (tsb && (irb->scsw.tm.fcxs == 0x01)) { + if (tsb) { len += sprintf(page + len, KERN_ERR PRINTK_HEADER " tsb->length %d\n", tsb->length); len += sprintf(page + len, KERN_ERR PRINTK_HEADER -- cgit From 9c95258c0d5911ae263bf50d854e402ce973ab32 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 24 Mar 2010 11:49:55 +0100 Subject: [S390] sclp: avoid 64 bit division Avoid 64 bit division to fix this compile error on 32 bit: drivers/s390/char/sclp_cmd.c:711: undefined reference to `__udivdi3' Also move the whole arch_get_memory_phys_device function to the memory hotplug related functions. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp_cmd.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index fc7ae05ce48..4b60ede07f0 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -308,6 +308,13 @@ struct assign_storage_sccb { u16 rn; } __packed; +int arch_get_memory_phys_device(unsigned long start_pfn) +{ + if (!rzm) + return 0; + return PFN_PHYS(start_pfn) >> ilog2(rzm); +} + static unsigned long long rn2addr(u16 rn) { return (unsigned long long) (rn - 1) * rzm; @@ -704,13 +711,6 @@ int sclp_chp_deconfigure(struct chp_id chpid) return do_chp_configure(SCLP_CMDW_DECONFIGURE_CHPATH | chpid.id << 8); } -int arch_get_memory_phys_device(unsigned long start_pfn) -{ - if (!rzm) - return 0; - return PFN_PHYS(start_pfn) / rzm; -} - struct chp_info_sccb { struct sccb_header header; u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; -- cgit From 7b26d82f5ea7de5667f87bb5ac6570111d7bff9f Mon Sep 17 00:00:00 2001 From: Hans-Joachim Picht Date: Wed, 24 Mar 2010 11:49:56 +0100 Subject: [S390] fix broken proc interface for sclp_async This patch now allows the use of the proc interface to either activate or deactivate call home on panic. e.g. echo 1 > /proc/sys/kernel/callhome strict_strtoul() requires _either_'\n\0' _or_ '\0' termination. This was missing and therefore the interface did not recognise valid input. Signed-off-by: Hans-Joachim Picht Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp_async.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/s390/char/sclp_async.c b/drivers/s390/char/sclp_async.c index 740fe405c39..f449c696e50 100644 --- a/drivers/s390/char/sclp_async.c +++ b/drivers/s390/char/sclp_async.c @@ -84,6 +84,7 @@ static int proc_handler_callhome(struct ctl_table *ctl, int write, rc = copy_from_user(buf, buffer, sizeof(buf)); if (rc != 0) return -EFAULT; + buf[len - 1] = '\0'; if (strict_strtoul(buf, 0, &val) != 0) return -EINVAL; if (val != 0 && val != 1) -- cgit From 06c0dd72aea3985d0ea036f771ebb1d1083bf69f Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Wed, 24 Mar 2010 11:49:57 +0100 Subject: [S390] fix boot failures with compressed kernels Fix two bugs with the kernel image compression: 1) reset the bss section of the compressed vmlinux 2) clear the high half of the registers for 64 bit early enough for the decompression step Signed-off-by: Martin Schwidefsky --- arch/s390/boot/compressed/misc.c | 8 ++++---- arch/s390/kernel/head.S | 3 +++ arch/s390/kernel/head64.S | 2 -- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c index a97d6952582..14e0479d388 100644 --- a/arch/s390/boot/compressed/misc.c +++ b/arch/s390/boot/compressed/misc.c @@ -24,8 +24,8 @@ /* Symbols defined by linker scripts */ extern char input_data[]; extern int input_len; -extern int _text; -extern int _end; +extern char _text, _end; +extern char _bss, _ebss; static void error(char *m); @@ -129,12 +129,12 @@ unsigned long decompress_kernel(void) unsigned long output_addr; unsigned char *output; + check_ipl_parmblock((void *) 0, (unsigned long) output + SZ__bss_start); + memset(&_bss, 0, &_ebss - &_bss); free_mem_ptr = (unsigned long)&_end; free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; output = (unsigned char *) ((free_mem_end_ptr + 4095UL) & -4096UL); - check_ipl_parmblock((void *) 0, (unsigned long) output + SZ__bss_start); - #ifdef CONFIG_BLK_DEV_INITRD /* * Move the initrd right behind the end of the decompressed diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S index ca4a62bd862..9d1f76702d4 100644 --- a/arch/s390/kernel/head.S +++ b/arch/s390/kernel/head.S @@ -517,7 +517,10 @@ startup: 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) diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S index 39580e76865..1f70970de0a 100644 --- a/arch/s390/kernel/head64.S +++ b/arch/s390/kernel/head64.S @@ -21,7 +21,6 @@ startup_continue: larl %r1,sched_clock_base_cc mvc 0(8,%r1),__LC_LAST_UPDATE_CLOCK larl %r13,.LPG1 # get base - lmh %r0,%r15,.Lzero64-.LPG1(%r13) # clear high-order half lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers lg %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area # move IPL device to lowcore @@ -67,7 +66,6 @@ startup_continue: .L4malign:.quad 0xffffffffffc00000 .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 .Lnop: .long 0x07000700 -.Lzero64:.fill 16,4,0x0 .Lparmaddr: .quad PARMAREA .align 64 -- cgit