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
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 * @req: sclp request
35 * @data: pointer to struct completion
36 */
37static 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 */
52static 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 */
88static 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
158out_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 */
174ssize_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
225out_unlock:
226	return len;
227}
228
229/*
230 * ET7 event listener
231 */
232static 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 */
242int 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 */
275void sclp_ftp_shutdown(void)
276{
277	sclp_unregister(&sclp_ftp_event);
278}
279