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