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