X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fs390%2Fkernel%2Fcpcmd.c;h=a5972f1541fea7525afc6c12f868cb7aa5d0efbd;hb=refs%2Fheads%2Fvserver;hp=d48351583ecb58ef2ec512eeaf006ed4ae7a2ca9;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/s390/kernel/cpcmd.c b/arch/s390/kernel/cpcmd.c index d48351583..a5972f154 100644 --- a/arch/s390/kernel/cpcmd.c +++ b/arch/s390/kernel/cpcmd.c @@ -2,78 +2,112 @@ * arch/s390/kernel/cpcmd.c * * S390 version - * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2005 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Christian Borntraeger (cborntra@de.ibm.com), */ -#include #include +#include +#include +#include +#include #include #include -#include #include #include -static spinlock_t cpcmd_lock = SPIN_LOCK_UNLOCKED; -static char cpcmd_buf[128]; +static DEFINE_SPINLOCK(cpcmd_lock); +static char cpcmd_buf[241]; -void cpcmd(char *cmd, char *response, int rlen) +/* + * __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) { - const int mask = 0x40000000L; - unsigned long flags; - int cmdlen; + unsigned cmdlen; + int return_code, return_len; - spin_lock_irqsave(&cpcmd_lock, flags); - cmdlen = strlen(cmd); - strcpy(cpcmd_buf, cmd); - ASCEBC(cpcmd_buf, cmdlen); + cmdlen = strlen(cmd); + BUG_ON(cmdlen > 240); + memcpy(cpcmd_buf, cmd, cmdlen); + ASCEBC(cpcmd_buf, cmdlen); - if (response != NULL && rlen > 0) { -#ifndef CONFIG_ARCH_S390X - asm volatile ("LRA 2,0(%0)\n\t" - "LR 4,%1\n\t" - "O 4,%4\n\t" - "LRA 3,0(%2)\n\t" - "LR 5,%3\n\t" - ".long 0x83240008 # Diagnose 83\n\t" - : /* no output */ - : "a" (cpcmd_buf), "d" (cmdlen), - "a" (response), "d" (rlen), "m" (mask) - : "2", "3", "4", "5" ); -#else /* CONFIG_ARCH_S390X */ - asm volatile (" lrag 2,0(%0)\n" - " lgr 4,%1\n" - " o 4,%4\n" - " lrag 3,0(%2)\n" - " lgr 5,%3\n" - " sam31\n" - " .long 0x83240008 # Diagnose 83\n" - " sam64" - : /* no output */ - : "a" (cpcmd_buf), "d" (cmdlen), - "a" (response), "d" (rlen), "m" (mask) - : "2", "3", "4", "5" ); -#endif /* CONFIG_ARCH_S390X */ + if (response != NULL && rlen > 0) { + 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; + + memset(response, 0, 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"); + return_code = (int) reg4; + return_len = (int) reg5; EBCASC(response, rlen); } else { -#ifndef CONFIG_ARCH_S390X - asm volatile ("LRA 2,0(%0)\n\t" - "LR 3,%1\n\t" - ".long 0x83230008 # Diagnose 83\n\t" - : /* no output */ - : "a" (cpcmd_buf), "d" (cmdlen) - : "2", "3" ); -#else /* CONFIG_ARCH_S390X */ - asm volatile (" lrag 2,0(%0)\n" - " lgr 3,%1\n" - " sam31\n" - " .long 0x83230008 # Diagnose 83\n" - " sam64" - : /* no output */ - : "a" (cpcmd_buf), "d" (cmdlen) - : "2", "3" ); -#endif /* CONFIG_ARCH_S390X */ + register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf; + register unsigned long reg3 asm ("3") = cmdlen; + return_len = 0; + 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_code = (int) reg3; } - spin_unlock_irqrestore(&cpcmd_lock, flags); + if (response_code != NULL) + *response_code = return_code; + return return_len; +} + +EXPORT_SYMBOL(__cpcmd); + +int cpcmd(const char *cmd, char *response, int rlen, int *response_code) +{ + char *lowbuf; + int len; + unsigned long flags; + + if ((rlen == 0) || (response == NULL) + || !((unsigned long)response >> 31)) { + spin_lock_irqsave(&cpcmd_lock, flags); + len = __cpcmd(cmd, response, rlen, response_code); + spin_unlock_irqrestore(&cpcmd_lock, flags); + } + else { + lowbuf = kmalloc(rlen, GFP_KERNEL | GFP_DMA); + if (!lowbuf) { + printk(KERN_WARNING + "cpcmd: could not allocate 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); + } + return len; } +EXPORT_SYMBOL(cpcmd);