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