Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *  S390 version
0004  *    Copyright IBM Corp. 1999, 2007
0005  *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
0006  *               Christian Borntraeger (cborntra@de.ibm.com),
0007  */
0008 
0009 #define KMSG_COMPONENT "cpcmd"
0010 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0011 
0012 #include <linux/kernel.h>
0013 #include <linux/export.h>
0014 #include <linux/slab.h>
0015 #include <linux/spinlock.h>
0016 #include <linux/stddef.h>
0017 #include <linux/string.h>
0018 #include <linux/mm.h>
0019 #include <asm/diag.h>
0020 #include <asm/ebcdic.h>
0021 #include <asm/cpcmd.h>
0022 #include <asm/io.h>
0023 
0024 static DEFINE_SPINLOCK(cpcmd_lock);
0025 static char cpcmd_buf[241];
0026 
0027 static int diag8_noresponse(int cmdlen)
0028 {
0029     asm volatile(
0030         "   diag    %[rx],%[ry],0x8\n"
0031         : [ry] "+&d" (cmdlen)
0032         : [rx] "d" (__pa(cpcmd_buf))
0033         : "cc");
0034     return cmdlen;
0035 }
0036 
0037 static int diag8_response(int cmdlen, char *response, int *rlen)
0038 {
0039     union register_pair rx, ry;
0040     int cc;
0041 
0042     rx.even = __pa(cpcmd_buf);
0043     rx.odd  = __pa(response);
0044     ry.even = cmdlen | 0x40000000L;
0045     ry.odd  = *rlen;
0046     asm volatile(
0047         "   diag    %[rx],%[ry],0x8\n"
0048         "   ipm %[cc]\n"
0049         "   srl %[cc],28\n"
0050         : [cc] "=&d" (cc), [ry] "+&d" (ry.pair)
0051         : [rx] "d" (rx.pair)
0052         : "cc");
0053     if (cc)
0054         *rlen += ry.odd;
0055     else
0056         *rlen = ry.odd;
0057     return ry.even;
0058 }
0059 
0060 /*
0061  * __cpcmd has some restrictions over cpcmd
0062  *  - __cpcmd is unlocked and therefore not SMP-safe
0063  */
0064 int  __cpcmd(const char *cmd, char *response, int rlen, int *response_code)
0065 {
0066     int cmdlen;
0067     int rc;
0068     int response_len;
0069 
0070     cmdlen = strlen(cmd);
0071     BUG_ON(cmdlen > 240);
0072     memcpy(cpcmd_buf, cmd, cmdlen);
0073     ASCEBC(cpcmd_buf, cmdlen);
0074 
0075     diag_stat_inc(DIAG_STAT_X008);
0076     if (response) {
0077         memset(response, 0, rlen);
0078         response_len = rlen;
0079         rc = diag8_response(cmdlen, response, &rlen);
0080         EBCASC(response, response_len);
0081         } else {
0082         rc = diag8_noresponse(cmdlen);
0083         }
0084     if (response_code)
0085         *response_code = rc;
0086     return rlen;
0087 }
0088 EXPORT_SYMBOL(__cpcmd);
0089 
0090 int cpcmd(const char *cmd, char *response, int rlen, int *response_code)
0091 {
0092     unsigned long flags;
0093     char *lowbuf;
0094     int len;
0095 
0096     if (is_vmalloc_or_module_addr(response)) {
0097         lowbuf = kmalloc(rlen, GFP_KERNEL);
0098         if (!lowbuf) {
0099             pr_warn("The cpcmd kernel function failed to allocate a response buffer\n");
0100             return -ENOMEM;
0101         }
0102         spin_lock_irqsave(&cpcmd_lock, flags);
0103         len = __cpcmd(cmd, lowbuf, rlen, response_code);
0104         spin_unlock_irqrestore(&cpcmd_lock, flags);
0105         memcpy(response, lowbuf, rlen);
0106         kfree(lowbuf);
0107     } else {
0108         spin_lock_irqsave(&cpcmd_lock, flags);
0109         len = __cpcmd(cmd, response, rlen, response_code);
0110         spin_unlock_irqrestore(&cpcmd_lock, flags);
0111     }
0112     return len;
0113 }
0114 EXPORT_SYMBOL(cpcmd);