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