xref: /linux/drivers/s390/char/sclp_ftp.c (revision 498495dba268b20e8eadd7fe93c140c68b6cc9d2)
1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
28f933b10SRalf Hoppe /*
38f933b10SRalf Hoppe  *    SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR
48f933b10SRalf Hoppe  *
58f933b10SRalf Hoppe  *    Copyright IBM Corp. 2013
68f933b10SRalf Hoppe  *    Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
78f933b10SRalf Hoppe  *
88f933b10SRalf Hoppe  */
98f933b10SRalf Hoppe 
108f933b10SRalf Hoppe #define KMSG_COMPONENT "hmcdrv"
118f933b10SRalf Hoppe #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
128f933b10SRalf Hoppe 
138f933b10SRalf Hoppe #include <linux/kernel.h>
148f933b10SRalf Hoppe #include <linux/mm.h>
158f933b10SRalf Hoppe #include <linux/slab.h>
168f933b10SRalf Hoppe #include <linux/io.h>
178f933b10SRalf Hoppe #include <linux/wait.h>
188f933b10SRalf Hoppe #include <linux/string.h>
198f933b10SRalf Hoppe #include <linux/jiffies.h>
208f933b10SRalf Hoppe #include <asm/sysinfo.h>
218f933b10SRalf Hoppe #include <asm/ebcdic.h>
228f933b10SRalf Hoppe 
238f933b10SRalf Hoppe #include "sclp.h"
248f933b10SRalf Hoppe #include "sclp_diag.h"
258f933b10SRalf Hoppe #include "sclp_ftp.h"
268f933b10SRalf Hoppe 
278f933b10SRalf Hoppe static DECLARE_COMPLETION(sclp_ftp_rx_complete);
288f933b10SRalf Hoppe static u8 sclp_ftp_ldflg;
298f933b10SRalf Hoppe static u64 sclp_ftp_fsize;
308f933b10SRalf Hoppe static u64 sclp_ftp_length;
318f933b10SRalf Hoppe 
328f933b10SRalf Hoppe /**
338f933b10SRalf Hoppe  * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback
348f933b10SRalf Hoppe  */
358f933b10SRalf Hoppe static void sclp_ftp_txcb(struct sclp_req *req, void *data)
368f933b10SRalf Hoppe {
378f933b10SRalf Hoppe 	struct completion *completion = data;
388f933b10SRalf Hoppe 
398f933b10SRalf Hoppe #ifdef DEBUG
408f933b10SRalf Hoppe 	pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n",
418f933b10SRalf Hoppe 		 req->sccb, 24, req->sccb);
428f933b10SRalf Hoppe #endif
438f933b10SRalf Hoppe 	complete(completion);
448f933b10SRalf Hoppe }
458f933b10SRalf Hoppe 
468f933b10SRalf Hoppe /**
478f933b10SRalf Hoppe  * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback
488f933b10SRalf Hoppe  */
498f933b10SRalf Hoppe static void sclp_ftp_rxcb(struct evbuf_header *evbuf)
508f933b10SRalf Hoppe {
518f933b10SRalf Hoppe 	struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf;
528f933b10SRalf Hoppe 
538f933b10SRalf Hoppe 	/*
548f933b10SRalf Hoppe 	 * Check for Diagnostic Test FTP Service
558f933b10SRalf Hoppe 	 */
568f933b10SRalf Hoppe 	if (evbuf->type != EVTYP_DIAG_TEST ||
578f933b10SRalf Hoppe 	    diag->route != SCLP_DIAG_FTP_ROUTE ||
588f933b10SRalf Hoppe 	    diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX ||
598f933b10SRalf Hoppe 	    evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN)
608f933b10SRalf Hoppe 		return;
618f933b10SRalf Hoppe 
628f933b10SRalf Hoppe #ifdef DEBUG
638f933b10SRalf Hoppe 	pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n",
648f933b10SRalf Hoppe 		 evbuf, 24, evbuf);
658f933b10SRalf Hoppe #endif
668f933b10SRalf Hoppe 
678f933b10SRalf Hoppe 	/*
688f933b10SRalf Hoppe 	 * Because the event buffer is located in a page which is owned
698f933b10SRalf Hoppe 	 * by the SCLP core, all data of interest must be copied. The
708f933b10SRalf Hoppe 	 * error indication is in 'sclp_ftp_ldflg'
718f933b10SRalf Hoppe 	 */
728f933b10SRalf Hoppe 	sclp_ftp_ldflg = diag->mdd.ftp.ldflg;
738f933b10SRalf Hoppe 	sclp_ftp_fsize = diag->mdd.ftp.fsize;
748f933b10SRalf Hoppe 	sclp_ftp_length = diag->mdd.ftp.length;
758f933b10SRalf Hoppe 
768f933b10SRalf Hoppe 	complete(&sclp_ftp_rx_complete);
778f933b10SRalf Hoppe }
788f933b10SRalf Hoppe 
798f933b10SRalf Hoppe /**
808f933b10SRalf Hoppe  * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request
818f933b10SRalf Hoppe  * @ftp: pointer to FTP descriptor
828f933b10SRalf Hoppe  *
838f933b10SRalf Hoppe  * Return: 0 on success, else a (negative) error code
848f933b10SRalf Hoppe  */
858f933b10SRalf Hoppe static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp)
868f933b10SRalf Hoppe {
878f933b10SRalf Hoppe 	struct completion completion;
888f933b10SRalf Hoppe 	struct sclp_diag_sccb *sccb;
898f933b10SRalf Hoppe 	struct sclp_req *req;
908f933b10SRalf Hoppe 	size_t len;
918f933b10SRalf Hoppe 	int rc;
928f933b10SRalf Hoppe 
938f933b10SRalf Hoppe 	req = kzalloc(sizeof(*req), GFP_KERNEL);
948f933b10SRalf Hoppe 	sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
958f933b10SRalf Hoppe 	if (!req || !sccb) {
968f933b10SRalf Hoppe 		rc = -ENOMEM;
978f933b10SRalf Hoppe 		goto out_free;
988f933b10SRalf Hoppe 	}
998f933b10SRalf Hoppe 
1008f933b10SRalf Hoppe 	sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN +
1018f933b10SRalf Hoppe 		sizeof(struct sccb_header);
1028f933b10SRalf Hoppe 	sccb->evbuf.hdr.type = EVTYP_DIAG_TEST;
1038f933b10SRalf Hoppe 	sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN;
1048f933b10SRalf Hoppe 	sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */
1058f933b10SRalf Hoppe 	sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE;
1068f933b10SRalf Hoppe 	sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX;
1078f933b10SRalf Hoppe 	sccb->evbuf.mdd.ftp.srcflg = 0;
1088f933b10SRalf Hoppe 	sccb->evbuf.mdd.ftp.pgsize = 0;
1098f933b10SRalf Hoppe 	sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE;
1108f933b10SRalf Hoppe 	sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL;
1118f933b10SRalf Hoppe 	sccb->evbuf.mdd.ftp.fsize = 0;
1128f933b10SRalf Hoppe 	sccb->evbuf.mdd.ftp.cmd = ftp->id;
1138f933b10SRalf Hoppe 	sccb->evbuf.mdd.ftp.offset = ftp->ofs;
1148f933b10SRalf Hoppe 	sccb->evbuf.mdd.ftp.length = ftp->len;
1158f933b10SRalf Hoppe 	sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf);
1168f933b10SRalf Hoppe 
1178f933b10SRalf Hoppe 	len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname,
1188f933b10SRalf Hoppe 		      HMCDRV_FTP_FIDENT_MAX);
1198f933b10SRalf Hoppe 	if (len >= HMCDRV_FTP_FIDENT_MAX) {
1208f933b10SRalf Hoppe 		rc = -EINVAL;
1218f933b10SRalf Hoppe 		goto out_free;
1228f933b10SRalf Hoppe 	}
1238f933b10SRalf Hoppe 
1248f933b10SRalf Hoppe 	req->command = SCLP_CMDW_WRITE_EVENT_DATA;
1258f933b10SRalf Hoppe 	req->sccb = sccb;
1268f933b10SRalf Hoppe 	req->status = SCLP_REQ_FILLED;
1278f933b10SRalf Hoppe 	req->callback = sclp_ftp_txcb;
1288f933b10SRalf Hoppe 	req->callback_data = &completion;
1298f933b10SRalf Hoppe 
1308f933b10SRalf Hoppe 	init_completion(&completion);
1318f933b10SRalf Hoppe 
1328f933b10SRalf Hoppe 	rc = sclp_add_request(req);
1338f933b10SRalf Hoppe 	if (rc)
1348f933b10SRalf Hoppe 		goto out_free;
1358f933b10SRalf Hoppe 
1368f933b10SRalf Hoppe 	/* Wait for end of ftp sclp command. */
1378f933b10SRalf Hoppe 	wait_for_completion(&completion);
1388f933b10SRalf Hoppe 
1398f933b10SRalf Hoppe #ifdef DEBUG
1408f933b10SRalf Hoppe 	pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n",
1418f933b10SRalf Hoppe 		 sccb->hdr.response_code, sccb->evbuf.hdr.flags);
1428f933b10SRalf Hoppe #endif
1438f933b10SRalf Hoppe 
1448f933b10SRalf Hoppe 	/*
1458f933b10SRalf Hoppe 	 * Check if sclp accepted the request. The data transfer runs
1468f933b10SRalf Hoppe 	 * asynchronously and the completion is indicated with an
1478f933b10SRalf Hoppe 	 * sclp ET7 event.
1488f933b10SRalf Hoppe 	 */
1498f933b10SRalf Hoppe 	if (req->status != SCLP_REQ_DONE ||
1508f933b10SRalf Hoppe 	    (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */
1518f933b10SRalf Hoppe 	    (sccb->hdr.response_code & 0xffU) != 0x20U) {
1528f933b10SRalf Hoppe 		rc = -EIO;
1538f933b10SRalf Hoppe 	}
1548f933b10SRalf Hoppe 
1558f933b10SRalf Hoppe out_free:
1568f933b10SRalf Hoppe 	free_page((unsigned long) sccb);
1578f933b10SRalf Hoppe 	kfree(req);
1588f933b10SRalf Hoppe 	return rc;
1598f933b10SRalf Hoppe }
1608f933b10SRalf Hoppe 
1618f933b10SRalf Hoppe /**
1628f933b10SRalf Hoppe  * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command
1638f933b10SRalf Hoppe  * @ftp: pointer to FTP command specification
1648f933b10SRalf Hoppe  * @fsize: return of file size (or NULL if undesirable)
1658f933b10SRalf Hoppe  *
1668f933b10SRalf Hoppe  * Attention: Notice that this function is not reentrant - so the caller
1678f933b10SRalf Hoppe  * must ensure locking.
1688f933b10SRalf Hoppe  *
1698f933b10SRalf Hoppe  * Return: number of bytes read/written or a (negative) error code
1708f933b10SRalf Hoppe  */
1718f933b10SRalf Hoppe ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
1728f933b10SRalf Hoppe {
1738f933b10SRalf Hoppe 	ssize_t len;
1748f933b10SRalf Hoppe #ifdef DEBUG
1758f933b10SRalf Hoppe 	unsigned long start_jiffies;
1768f933b10SRalf Hoppe 
1778f933b10SRalf Hoppe 	pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n",
1788f933b10SRalf Hoppe 		 ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len);
1798f933b10SRalf Hoppe 	start_jiffies = jiffies;
1808f933b10SRalf Hoppe #endif
1818f933b10SRalf Hoppe 
1828f933b10SRalf Hoppe 	init_completion(&sclp_ftp_rx_complete);
1838f933b10SRalf Hoppe 
1848f933b10SRalf Hoppe 	/* Start ftp sclp command. */
1858f933b10SRalf Hoppe 	len = sclp_ftp_et7(ftp);
1868f933b10SRalf Hoppe 	if (len)
1878f933b10SRalf Hoppe 		goto out_unlock;
1888f933b10SRalf Hoppe 
1898f933b10SRalf Hoppe 	/*
1908f933b10SRalf Hoppe 	 * There is no way to cancel the sclp ET7 request, the code
1918f933b10SRalf Hoppe 	 * needs to wait unconditionally until the transfer is complete.
1928f933b10SRalf Hoppe 	 */
1938f933b10SRalf Hoppe 	wait_for_completion(&sclp_ftp_rx_complete);
1948f933b10SRalf Hoppe 
1958f933b10SRalf Hoppe #ifdef DEBUG
1968f933b10SRalf Hoppe 	pr_debug("completed SCLP (ET7) request after %lu ms (all)\n",
1978f933b10SRalf Hoppe 		 (jiffies - start_jiffies) * 1000 / HZ);
1988f933b10SRalf Hoppe 	pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n",
1998f933b10SRalf Hoppe 		 sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize);
2008f933b10SRalf Hoppe #endif
2018f933b10SRalf Hoppe 
2028f933b10SRalf Hoppe 	switch (sclp_ftp_ldflg) {
2038f933b10SRalf Hoppe 	case SCLP_DIAG_FTP_OK:
2048f933b10SRalf Hoppe 		len = sclp_ftp_length;
2058f933b10SRalf Hoppe 		if (fsize)
2068f933b10SRalf Hoppe 			*fsize = sclp_ftp_fsize;
2078f933b10SRalf Hoppe 		break;
2088f933b10SRalf Hoppe 	case SCLP_DIAG_FTP_LDNPERM:
2098f933b10SRalf Hoppe 		len = -EPERM;
2108f933b10SRalf Hoppe 		break;
2118f933b10SRalf Hoppe 	case SCLP_DIAG_FTP_LDRUNS:
2128f933b10SRalf Hoppe 		len = -EBUSY;
2138f933b10SRalf Hoppe 		break;
2148f933b10SRalf Hoppe 	case SCLP_DIAG_FTP_LDFAIL:
2158f933b10SRalf Hoppe 		len = -ENOENT;
2168f933b10SRalf Hoppe 		break;
2178f933b10SRalf Hoppe 	default:
2188f933b10SRalf Hoppe 		len = -EIO;
2198f933b10SRalf Hoppe 		break;
2208f933b10SRalf Hoppe 	}
2218f933b10SRalf Hoppe 
2228f933b10SRalf Hoppe out_unlock:
2238f933b10SRalf Hoppe 	return len;
2248f933b10SRalf Hoppe }
2258f933b10SRalf Hoppe 
2268f933b10SRalf Hoppe /*
2278f933b10SRalf Hoppe  * ET7 event listener
2288f933b10SRalf Hoppe  */
2298f933b10SRalf Hoppe static struct sclp_register sclp_ftp_event = {
2308f933b10SRalf Hoppe 	.send_mask = EVTYP_DIAG_TEST_MASK,    /* want tx events */
2318f933b10SRalf Hoppe 	.receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */
2328f933b10SRalf Hoppe 	.receiver_fn = sclp_ftp_rxcb,	      /* async callback (rx) */
2338f933b10SRalf Hoppe 	.state_change_fn = NULL,
2348f933b10SRalf Hoppe 	.pm_event_fn = NULL,
2358f933b10SRalf Hoppe };
2368f933b10SRalf Hoppe 
2378f933b10SRalf Hoppe /**
2388f933b10SRalf Hoppe  * sclp_ftp_startup() - startup of FTP services, when running on LPAR
2398f933b10SRalf Hoppe  */
2408f933b10SRalf Hoppe int sclp_ftp_startup(void)
2418f933b10SRalf Hoppe {
2428f933b10SRalf Hoppe #ifdef DEBUG
2438f933b10SRalf Hoppe 	unsigned long info;
2448f933b10SRalf Hoppe #endif
2458f933b10SRalf Hoppe 	int rc;
2468f933b10SRalf Hoppe 
2478f933b10SRalf Hoppe 	rc = sclp_register(&sclp_ftp_event);
2488f933b10SRalf Hoppe 	if (rc)
2498f933b10SRalf Hoppe 		return rc;
2508f933b10SRalf Hoppe 
2518f933b10SRalf Hoppe #ifdef DEBUG
2528f933b10SRalf Hoppe 	info = get_zeroed_page(GFP_KERNEL);
2538f933b10SRalf Hoppe 
2548f933b10SRalf Hoppe 	if (info != 0) {
2558f933b10SRalf Hoppe 		struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info;
2568f933b10SRalf Hoppe 
2578f933b10SRalf Hoppe 		if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */
2588f933b10SRalf Hoppe 			info222->name[sizeof(info222->name) - 1] = '\0';
2598f933b10SRalf Hoppe 			EBCASC_500(info222->name, sizeof(info222->name) - 1);
2608f933b10SRalf Hoppe 			pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n",
2618f933b10SRalf Hoppe 				 info222->lpar_number, info222->name);
2628f933b10SRalf Hoppe 		}
2638f933b10SRalf Hoppe 
2648f933b10SRalf Hoppe 		free_page(info);
2658f933b10SRalf Hoppe 	}
2668f933b10SRalf Hoppe #endif	/* DEBUG */
2678f933b10SRalf Hoppe 	return 0;
2688f933b10SRalf Hoppe }
2698f933b10SRalf Hoppe 
2708f933b10SRalf Hoppe /**
2718f933b10SRalf Hoppe  * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR
2728f933b10SRalf Hoppe  */
2738f933b10SRalf Hoppe void sclp_ftp_shutdown(void)
2748f933b10SRalf Hoppe {
2758f933b10SRalf Hoppe 	sclp_unregister(&sclp_ftp_event);
2768f933b10SRalf Hoppe }
277