linux/arch/s390/kernel/cpcmd.c
<<
>>
Prefs
   1/*
   2 *  S390 version
   3 *    Copyright IBM Corp. 1999, 2007
   4 *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
   5 *               Christian Borntraeger (cborntra@de.ibm.com),
   6 */
   7
   8#define KMSG_COMPONENT "cpcmd"
   9#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
  10
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/slab.h>
  14#include <linux/spinlock.h>
  15#include <linux/stddef.h>
  16#include <linux/string.h>
  17#include <asm/ebcdic.h>
  18#include <asm/cpcmd.h>
  19#include <asm/io.h>
  20
  21static DEFINE_SPINLOCK(cpcmd_lock);
  22static char cpcmd_buf[241];
  23
  24static int diag8_noresponse(int cmdlen)
  25{
  26        register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf;
  27        register unsigned long reg3 asm ("3") = cmdlen;
  28
  29        asm volatile(
  30#ifndef CONFIG_64BIT
  31                "       diag    %1,%0,0x8\n"
  32#else /* CONFIG_64BIT */
  33                "       sam31\n"
  34                "       diag    %1,%0,0x8\n"
  35                "       sam64\n"
  36#endif /* CONFIG_64BIT */
  37                : "+d" (reg3) : "d" (reg2) : "cc");
  38        return reg3;
  39}
  40
  41static int diag8_response(int cmdlen, char *response, int *rlen)
  42{
  43        register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf;
  44        register unsigned long reg3 asm ("3") = (addr_t) response;
  45        register unsigned long reg4 asm ("4") = cmdlen | 0x40000000L;
  46        register unsigned long reg5 asm ("5") = *rlen;
  47
  48        asm volatile(
  49#ifndef CONFIG_64BIT
  50                "       diag    %2,%0,0x8\n"
  51                "       brc     8,1f\n"
  52                "       ar      %1,%4\n"
  53#else /* CONFIG_64BIT */
  54                "       sam31\n"
  55                "       diag    %2,%0,0x8\n"
  56                "       sam64\n"
  57                "       brc     8,1f\n"
  58                "       agr     %1,%4\n"
  59#endif /* CONFIG_64BIT */
  60                "1:\n"
  61                : "+d" (reg4), "+d" (reg5)
  62                : "d" (reg2), "d" (reg3), "d" (*rlen) : "cc");
  63        *rlen = reg5;
  64        return reg4;
  65}
  66
  67/*
  68 * __cpcmd has some restrictions over cpcmd
  69 *  - the response buffer must reside below 2GB (if any)
  70 *  - __cpcmd is unlocked and therefore not SMP-safe
  71 */
  72int  __cpcmd(const char *cmd, char *response, int rlen, int *response_code)
  73{
  74        int cmdlen;
  75        int rc;
  76        int response_len;
  77
  78        cmdlen = strlen(cmd);
  79        BUG_ON(cmdlen > 240);
  80        memcpy(cpcmd_buf, cmd, cmdlen);
  81        ASCEBC(cpcmd_buf, cmdlen);
  82
  83        if (response) {
  84                memset(response, 0, rlen);
  85                response_len = rlen;
  86                rc = diag8_response(cmdlen, response, &rlen);
  87                EBCASC(response, response_len);
  88        } else {
  89                rc = diag8_noresponse(cmdlen);
  90        }
  91        if (response_code)
  92                *response_code = rc;
  93        return rlen;
  94}
  95EXPORT_SYMBOL(__cpcmd);
  96
  97int cpcmd(const char *cmd, char *response, int rlen, int *response_code)
  98{
  99        char *lowbuf;
 100        int len;
 101        unsigned long flags;
 102
 103        if ((virt_to_phys(response) != (unsigned long) response) ||
 104                        (((unsigned long)response + rlen) >> 31)) {
 105                lowbuf = kmalloc(rlen, GFP_KERNEL | GFP_DMA);
 106                if (!lowbuf) {
 107                        pr_warning("The cpcmd kernel function failed to "
 108                                   "allocate a response buffer\n");
 109                        return -ENOMEM;
 110                }
 111                spin_lock_irqsave(&cpcmd_lock, flags);
 112                len = __cpcmd(cmd, lowbuf, rlen, response_code);
 113                spin_unlock_irqrestore(&cpcmd_lock, flags);
 114                memcpy(response, lowbuf, rlen);
 115                kfree(lowbuf);
 116        } else {
 117                spin_lock_irqsave(&cpcmd_lock, flags);
 118                len = __cpcmd(cmd, response, rlen, response_code);
 119                spin_unlock_irqrestore(&cpcmd_lock, flags);
 120        }
 121        return len;
 122}
 123EXPORT_SYMBOL(cpcmd);
 124