• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
sclp_ftp_txcb(struct sclp_req * req,void * data)35 static 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  */
sclp_ftp_rxcb(struct evbuf_header * evbuf)49 static 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  */
sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec * ftp)85 static 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 
155 out_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  */
sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec * ftp,size_t * fsize)171 ssize_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 
222 out_unlock:
223 	return len;
224 }
225 
226 /*
227  * ET7 event listener
228  */
229 static 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  */
sclp_ftp_startup(void)240 int 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  */
sclp_ftp_shutdown(void)273 void sclp_ftp_shutdown(void)
274 {
275 	sclp_unregister(&sclp_ftp_event);
276 }
277