1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * S390 version 4 * Copyright IBM Corp. 1999, 2007 5 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), 6 * Christian Borntraeger (cborntra@de.ibm.com), 7 */ 8 9 #define pr_fmt(fmt) "cpcmd: " fmt 10 11 #include <linux/kernel.h> 12 #include <linux/export.h> 13 #include <linux/slab.h> 14 #include <linux/spinlock.h> 15 #include <linux/stddef.h> 16 #include <linux/string.h> 17 #include <linux/mm.h> 18 #include <linux/io.h> 19 #include <asm/diag.h> 20 #include <asm/ebcdic.h> 21 #include <asm/cpcmd.h> 22 #include <asm/asm.h> 23 24 static DEFINE_SPINLOCK(cpcmd_lock); 25 static char cpcmd_buf[241]; 26 27 static int diag8_noresponse(int cmdlen) 28 { 29 asm volatile( 30 " diag %[rx],%[ry],0x8\n" 31 : [ry] "+&d" (cmdlen) 32 : [rx] "d" (__pa(cpcmd_buf)) 33 : "cc"); 34 return cmdlen; 35 } 36 37 static int diag8_response(int cmdlen, char *response, int *rlen) 38 { 39 union register_pair rx, ry; 40 int cc; 41 42 rx.even = __pa(cpcmd_buf); 43 rx.odd = __pa(response); 44 ry.even = cmdlen | 0x40000000L; 45 ry.odd = *rlen; 46 asm volatile( 47 " diag %[rx],%[ry],0x8\n" 48 CC_IPM(cc) 49 : CC_OUT(cc, cc), [ry] "+d" (ry.pair) 50 : [rx] "d" (rx.pair) 51 : CC_CLOBBER); 52 if (CC_TRANSFORM(cc)) 53 *rlen += ry.odd; 54 else 55 *rlen = ry.odd; 56 return ry.even; 57 } 58 59 /* 60 * __cpcmd has some restrictions over cpcmd 61 * - __cpcmd is unlocked and therefore not SMP-safe 62 */ 63 int __cpcmd(const char *cmd, char *response, int rlen, int *response_code) 64 { 65 int cmdlen; 66 int rc; 67 int response_len; 68 69 cmdlen = strlen(cmd); 70 BUG_ON(cmdlen > 240); 71 memcpy(cpcmd_buf, cmd, cmdlen); 72 ASCEBC(cpcmd_buf, cmdlen); 73 74 diag_stat_inc(DIAG_STAT_X008); 75 if (response) { 76 memset(response, 0, rlen); 77 response_len = rlen; 78 rc = diag8_response(cmdlen, response, &rlen); 79 EBCASC(response, response_len); 80 } else { 81 rc = diag8_noresponse(cmdlen); 82 } 83 if (response_code) 84 *response_code = rc; 85 return rlen; 86 } 87 EXPORT_SYMBOL(__cpcmd); 88 89 int cpcmd(const char *cmd, char *response, int rlen, int *response_code) 90 { 91 unsigned long flags; 92 char *lowbuf; 93 int len; 94 95 if (is_vmalloc_or_module_addr(response)) { 96 lowbuf = kmalloc(rlen, GFP_KERNEL); 97 if (!lowbuf) { 98 pr_warn("The cpcmd kernel function failed to allocate a response buffer\n"); 99 return -ENOMEM; 100 } 101 spin_lock_irqsave(&cpcmd_lock, flags); 102 len = __cpcmd(cmd, lowbuf, rlen, response_code); 103 spin_unlock_irqrestore(&cpcmd_lock, flags); 104 memcpy(response, lowbuf, rlen); 105 kfree(lowbuf); 106 } else { 107 spin_lock_irqsave(&cpcmd_lock, flags); 108 len = __cpcmd(cmd, response, rlen, response_code); 109 spin_unlock_irqrestore(&cpcmd_lock, flags); 110 } 111 return len; 112 } 113 EXPORT_SYMBOL(cpcmd); 114