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