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