18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * S390 version 48c2ecf20Sopenharmony_ci * Copyright IBM Corp. 1999, 2007 58c2ecf20Sopenharmony_ci * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), 68c2ecf20Sopenharmony_ci * Christian Borntraeger (cborntra@de.ibm.com), 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "cpcmd" 108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/export.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 168c2ecf20Sopenharmony_ci#include <linux/stddef.h> 178c2ecf20Sopenharmony_ci#include <linux/string.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <asm/diag.h> 208c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 218c2ecf20Sopenharmony_ci#include <asm/cpcmd.h> 228c2ecf20Sopenharmony_ci#include <asm/io.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cpcmd_lock); 258c2ecf20Sopenharmony_cistatic char cpcmd_buf[241]; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int diag8_noresponse(int cmdlen) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf; 308c2ecf20Sopenharmony_ci register unsigned long reg3 asm ("3") = cmdlen; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci asm volatile( 338c2ecf20Sopenharmony_ci " diag %1,%0,0x8\n" 348c2ecf20Sopenharmony_ci : "+d" (reg3) : "d" (reg2) : "cc"); 358c2ecf20Sopenharmony_ci return reg3; 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic int diag8_response(int cmdlen, char *response, int *rlen) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci unsigned long _cmdlen = cmdlen | 0x40000000L; 418c2ecf20Sopenharmony_ci unsigned long _rlen = *rlen; 428c2ecf20Sopenharmony_ci register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf; 438c2ecf20Sopenharmony_ci register unsigned long reg3 asm ("3") = (addr_t) response; 448c2ecf20Sopenharmony_ci register unsigned long reg4 asm ("4") = _cmdlen; 458c2ecf20Sopenharmony_ci register unsigned long reg5 asm ("5") = _rlen; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci asm volatile( 488c2ecf20Sopenharmony_ci " diag %2,%0,0x8\n" 498c2ecf20Sopenharmony_ci " brc 8,1f\n" 508c2ecf20Sopenharmony_ci " agr %1,%4\n" 518c2ecf20Sopenharmony_ci "1:\n" 528c2ecf20Sopenharmony_ci : "+d" (reg4), "+d" (reg5) 538c2ecf20Sopenharmony_ci : "d" (reg2), "d" (reg3), "d" (*rlen) : "cc"); 548c2ecf20Sopenharmony_ci *rlen = reg5; 558c2ecf20Sopenharmony_ci return reg4; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* 598c2ecf20Sopenharmony_ci * __cpcmd has some restrictions over cpcmd 608c2ecf20Sopenharmony_ci * - __cpcmd is unlocked and therefore not SMP-safe 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ciint __cpcmd(const char *cmd, char *response, int rlen, int *response_code) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci int cmdlen; 658c2ecf20Sopenharmony_ci int rc; 668c2ecf20Sopenharmony_ci int response_len; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci cmdlen = strlen(cmd); 698c2ecf20Sopenharmony_ci BUG_ON(cmdlen > 240); 708c2ecf20Sopenharmony_ci memcpy(cpcmd_buf, cmd, cmdlen); 718c2ecf20Sopenharmony_ci ASCEBC(cpcmd_buf, cmdlen); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci diag_stat_inc(DIAG_STAT_X008); 748c2ecf20Sopenharmony_ci if (response) { 758c2ecf20Sopenharmony_ci memset(response, 0, rlen); 768c2ecf20Sopenharmony_ci response_len = rlen; 778c2ecf20Sopenharmony_ci rc = diag8_response(cmdlen, response, &rlen); 788c2ecf20Sopenharmony_ci EBCASC(response, response_len); 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci rc = diag8_noresponse(cmdlen); 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci if (response_code) 838c2ecf20Sopenharmony_ci *response_code = rc; 848c2ecf20Sopenharmony_ci return rlen; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__cpcmd); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ciint cpcmd(const char *cmd, char *response, int rlen, int *response_code) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci unsigned long flags; 918c2ecf20Sopenharmony_ci char *lowbuf; 928c2ecf20Sopenharmony_ci int len; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (is_vmalloc_or_module_addr(response)) { 958c2ecf20Sopenharmony_ci lowbuf = kmalloc(rlen, GFP_KERNEL); 968c2ecf20Sopenharmony_ci if (!lowbuf) { 978c2ecf20Sopenharmony_ci pr_warn("The cpcmd kernel function failed to allocate a response buffer\n"); 988c2ecf20Sopenharmony_ci return -ENOMEM; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci spin_lock_irqsave(&cpcmd_lock, flags); 1018c2ecf20Sopenharmony_ci len = __cpcmd(cmd, lowbuf, rlen, response_code); 1028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cpcmd_lock, flags); 1038c2ecf20Sopenharmony_ci memcpy(response, lowbuf, rlen); 1048c2ecf20Sopenharmony_ci kfree(lowbuf); 1058c2ecf20Sopenharmony_ci } else { 1068c2ecf20Sopenharmony_ci spin_lock_irqsave(&cpcmd_lock, flags); 1078c2ecf20Sopenharmony_ci len = __cpcmd(cmd, response, rlen, response_code); 1088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&cpcmd_lock, flags); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci return len; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cpcmd); 113