License cleanup: add SPDX GPL-2.0 license identifier to files with no license
[linux-block.git] / drivers / s390 / char / sclp_ftp.c
CommitLineData
b2441318 1// SPDX-License-Identifier: GPL-2.0
8f933b10
RH
2/*
3 * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR
4 *
5 * Copyright IBM Corp. 2013
6 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
7 *
8 */
9
10#define KMSG_COMPONENT "hmcdrv"
11#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
12
13#include <linux/kernel.h>
14#include <linux/mm.h>
15#include <linux/slab.h>
16#include <linux/io.h>
17#include <linux/wait.h>
18#include <linux/string.h>
19#include <linux/jiffies.h>
20#include <asm/sysinfo.h>
21#include <asm/ebcdic.h>
22
23#include "sclp.h"
24#include "sclp_diag.h"
25#include "sclp_ftp.h"
26
27static DECLARE_COMPLETION(sclp_ftp_rx_complete);
28static u8 sclp_ftp_ldflg;
29static u64 sclp_ftp_fsize;
30static u64 sclp_ftp_length;
31
32/**
33 * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback
34 */
35static void sclp_ftp_txcb(struct sclp_req *req, void *data)
36{
37 struct completion *completion = data;
38
39#ifdef DEBUG
40 pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n",
41 req->sccb, 24, req->sccb);
42#endif
43 complete(completion);
44}
45
46/**
47 * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback
48 */
49static void sclp_ftp_rxcb(struct evbuf_header *evbuf)
50{
51 struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf;
52
53 /*
54 * Check for Diagnostic Test FTP Service
55 */
56 if (evbuf->type != EVTYP_DIAG_TEST ||
57 diag->route != SCLP_DIAG_FTP_ROUTE ||
58 diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX ||
59 evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN)
60 return;
61
62#ifdef DEBUG
63 pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n",
64 evbuf, 24, evbuf);
65#endif
66
67 /*
68 * Because the event buffer is located in a page which is owned
69 * by the SCLP core, all data of interest must be copied. The
70 * error indication is in 'sclp_ftp_ldflg'
71 */
72 sclp_ftp_ldflg = diag->mdd.ftp.ldflg;
73 sclp_ftp_fsize = diag->mdd.ftp.fsize;
74 sclp_ftp_length = diag->mdd.ftp.length;
75
76 complete(&sclp_ftp_rx_complete);
77}
78
79/**
80 * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request
81 * @ftp: pointer to FTP descriptor
82 *
83 * Return: 0 on success, else a (negative) error code
84 */
85static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp)
86{
87 struct completion completion;
88 struct sclp_diag_sccb *sccb;
89 struct sclp_req *req;
90 size_t len;
91 int rc;
92
93 req = kzalloc(sizeof(*req), GFP_KERNEL);
94 sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
95 if (!req || !sccb) {
96 rc = -ENOMEM;
97 goto out_free;
98 }
99
100 sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN +
101 sizeof(struct sccb_header);
102 sccb->evbuf.hdr.type = EVTYP_DIAG_TEST;
103 sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN;
104 sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */
105 sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE;
106 sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX;
107 sccb->evbuf.mdd.ftp.srcflg = 0;
108 sccb->evbuf.mdd.ftp.pgsize = 0;
109 sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE;
110 sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL;
111 sccb->evbuf.mdd.ftp.fsize = 0;
112 sccb->evbuf.mdd.ftp.cmd = ftp->id;
113 sccb->evbuf.mdd.ftp.offset = ftp->ofs;
114 sccb->evbuf.mdd.ftp.length = ftp->len;
115 sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf);
116
117 len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname,
118 HMCDRV_FTP_FIDENT_MAX);
119 if (len >= HMCDRV_FTP_FIDENT_MAX) {
120 rc = -EINVAL;
121 goto out_free;
122 }
123
124 req->command = SCLP_CMDW_WRITE_EVENT_DATA;
125 req->sccb = sccb;
126 req->status = SCLP_REQ_FILLED;
127 req->callback = sclp_ftp_txcb;
128 req->callback_data = &completion;
129
130 init_completion(&completion);
131
132 rc = sclp_add_request(req);
133 if (rc)
134 goto out_free;
135
136 /* Wait for end of ftp sclp command. */
137 wait_for_completion(&completion);
138
139#ifdef DEBUG
140 pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n",
141 sccb->hdr.response_code, sccb->evbuf.hdr.flags);
142#endif
143
144 /*
145 * Check if sclp accepted the request. The data transfer runs
146 * asynchronously and the completion is indicated with an
147 * sclp ET7 event.
148 */
149 if (req->status != SCLP_REQ_DONE ||
150 (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */
151 (sccb->hdr.response_code & 0xffU) != 0x20U) {
152 rc = -EIO;
153 }
154
155out_free:
156 free_page((unsigned long) sccb);
157 kfree(req);
158 return rc;
159}
160
161/**
162 * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command
163 * @ftp: pointer to FTP command specification
164 * @fsize: return of file size (or NULL if undesirable)
165 *
166 * Attention: Notice that this function is not reentrant - so the caller
167 * must ensure locking.
168 *
169 * Return: number of bytes read/written or a (negative) error code
170 */
171ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
172{
173 ssize_t len;
174#ifdef DEBUG
175 unsigned long start_jiffies;
176
177 pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n",
178 ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len);
179 start_jiffies = jiffies;
180#endif
181
182 init_completion(&sclp_ftp_rx_complete);
183
184 /* Start ftp sclp command. */
185 len = sclp_ftp_et7(ftp);
186 if (len)
187 goto out_unlock;
188
189 /*
190 * There is no way to cancel the sclp ET7 request, the code
191 * needs to wait unconditionally until the transfer is complete.
192 */
193 wait_for_completion(&sclp_ftp_rx_complete);
194
195#ifdef DEBUG
196 pr_debug("completed SCLP (ET7) request after %lu ms (all)\n",
197 (jiffies - start_jiffies) * 1000 / HZ);
198 pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n",
199 sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize);
200#endif
201
202 switch (sclp_ftp_ldflg) {
203 case SCLP_DIAG_FTP_OK:
204 len = sclp_ftp_length;
205 if (fsize)
206 *fsize = sclp_ftp_fsize;
207 break;
208 case SCLP_DIAG_FTP_LDNPERM:
209 len = -EPERM;
210 break;
211 case SCLP_DIAG_FTP_LDRUNS:
212 len = -EBUSY;
213 break;
214 case SCLP_DIAG_FTP_LDFAIL:
215 len = -ENOENT;
216 break;
217 default:
218 len = -EIO;
219 break;
220 }
221
222out_unlock:
223 return len;
224}
225
226/*
227 * ET7 event listener
228 */
229static struct sclp_register sclp_ftp_event = {
230 .send_mask = EVTYP_DIAG_TEST_MASK, /* want tx events */
231 .receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */
232 .receiver_fn = sclp_ftp_rxcb, /* async callback (rx) */
233 .state_change_fn = NULL,
234 .pm_event_fn = NULL,
235};
236
237/**
238 * sclp_ftp_startup() - startup of FTP services, when running on LPAR
239 */
240int sclp_ftp_startup(void)
241{
242#ifdef DEBUG
243 unsigned long info;
244#endif
245 int rc;
246
247 rc = sclp_register(&sclp_ftp_event);
248 if (rc)
249 return rc;
250
251#ifdef DEBUG
252 info = get_zeroed_page(GFP_KERNEL);
253
254 if (info != 0) {
255 struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info;
256
257 if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */
258 info222->name[sizeof(info222->name) - 1] = '\0';
259 EBCASC_500(info222->name, sizeof(info222->name) - 1);
260 pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n",
261 info222->lpar_number, info222->name);
262 }
263
264 free_page(info);
265 }
266#endif /* DEBUG */
267 return 0;
268}
269
270/**
271 * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR
272 */
273void sclp_ftp_shutdown(void)
274{
275 sclp_unregister(&sclp_ftp_event);
276}