Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/s390/kernel/cpcmd.c
48907 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* S390 version
4
* Copyright IBM Corp. 1999, 2007
5
* Author(s): Martin Schwidefsky ([email protected]),
6
* Christian Borntraeger ([email protected]),
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
115