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