0001
0002
0003
0004
0005
0006
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
0062
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);