162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *    SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *    Copyright IBM Corp. 2013
662306a36Sopenharmony_ci *    Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define KMSG_COMPONENT "hmcdrv"
1162306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/mm.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/io.h>
1762306a36Sopenharmony_ci#include <linux/wait.h>
1862306a36Sopenharmony_ci#include <linux/string.h>
1962306a36Sopenharmony_ci#include <linux/jiffies.h>
2062306a36Sopenharmony_ci#include <asm/sysinfo.h>
2162306a36Sopenharmony_ci#include <asm/ebcdic.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "sclp.h"
2462306a36Sopenharmony_ci#include "sclp_diag.h"
2562306a36Sopenharmony_ci#include "sclp_ftp.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic DECLARE_COMPLETION(sclp_ftp_rx_complete);
2862306a36Sopenharmony_cistatic u8 sclp_ftp_ldflg;
2962306a36Sopenharmony_cistatic u64 sclp_ftp_fsize;
3062306a36Sopenharmony_cistatic u64 sclp_ftp_length;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/**
3362306a36Sopenharmony_ci * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback
3462306a36Sopenharmony_ci * @req: sclp request
3562306a36Sopenharmony_ci * @data: pointer to struct completion
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_cistatic void sclp_ftp_txcb(struct sclp_req *req, void *data)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	struct completion *completion = data;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#ifdef DEBUG
4262306a36Sopenharmony_ci	pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n",
4362306a36Sopenharmony_ci		 req->sccb, 24, req->sccb);
4462306a36Sopenharmony_ci#endif
4562306a36Sopenharmony_ci	complete(completion);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/**
4962306a36Sopenharmony_ci * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback
5062306a36Sopenharmony_ci * @evbuf: pointer to Diagnostic Test (ET7) event buffer
5162306a36Sopenharmony_ci */
5262306a36Sopenharmony_cistatic void sclp_ftp_rxcb(struct evbuf_header *evbuf)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/*
5762306a36Sopenharmony_ci	 * Check for Diagnostic Test FTP Service
5862306a36Sopenharmony_ci	 */
5962306a36Sopenharmony_ci	if (evbuf->type != EVTYP_DIAG_TEST ||
6062306a36Sopenharmony_ci	    diag->route != SCLP_DIAG_FTP_ROUTE ||
6162306a36Sopenharmony_ci	    diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX ||
6262306a36Sopenharmony_ci	    evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN)
6362306a36Sopenharmony_ci		return;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci#ifdef DEBUG
6662306a36Sopenharmony_ci	pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n",
6762306a36Sopenharmony_ci		 evbuf, 24, evbuf);
6862306a36Sopenharmony_ci#endif
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	 * Because the event buffer is located in a page which is owned
7262306a36Sopenharmony_ci	 * by the SCLP core, all data of interest must be copied. The
7362306a36Sopenharmony_ci	 * error indication is in 'sclp_ftp_ldflg'
7462306a36Sopenharmony_ci	 */
7562306a36Sopenharmony_ci	sclp_ftp_ldflg = diag->mdd.ftp.ldflg;
7662306a36Sopenharmony_ci	sclp_ftp_fsize = diag->mdd.ftp.fsize;
7762306a36Sopenharmony_ci	sclp_ftp_length = diag->mdd.ftp.length;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	complete(&sclp_ftp_rx_complete);
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/**
8362306a36Sopenharmony_ci * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request
8462306a36Sopenharmony_ci * @ftp: pointer to FTP descriptor
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * Return: 0 on success, else a (negative) error code
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_cistatic int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct completion completion;
9162306a36Sopenharmony_ci	struct sclp_diag_sccb *sccb;
9262306a36Sopenharmony_ci	struct sclp_req *req;
9362306a36Sopenharmony_ci	ssize_t len;
9462306a36Sopenharmony_ci	int rc;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	req = kzalloc(sizeof(*req), GFP_KERNEL);
9762306a36Sopenharmony_ci	sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
9862306a36Sopenharmony_ci	if (!req || !sccb) {
9962306a36Sopenharmony_ci		rc = -ENOMEM;
10062306a36Sopenharmony_ci		goto out_free;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN +
10462306a36Sopenharmony_ci		sizeof(struct sccb_header);
10562306a36Sopenharmony_ci	sccb->evbuf.hdr.type = EVTYP_DIAG_TEST;
10662306a36Sopenharmony_ci	sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN;
10762306a36Sopenharmony_ci	sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */
10862306a36Sopenharmony_ci	sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE;
10962306a36Sopenharmony_ci	sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX;
11062306a36Sopenharmony_ci	sccb->evbuf.mdd.ftp.srcflg = 0;
11162306a36Sopenharmony_ci	sccb->evbuf.mdd.ftp.pgsize = 0;
11262306a36Sopenharmony_ci	sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE;
11362306a36Sopenharmony_ci	sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL;
11462306a36Sopenharmony_ci	sccb->evbuf.mdd.ftp.fsize = 0;
11562306a36Sopenharmony_ci	sccb->evbuf.mdd.ftp.cmd = ftp->id;
11662306a36Sopenharmony_ci	sccb->evbuf.mdd.ftp.offset = ftp->ofs;
11762306a36Sopenharmony_ci	sccb->evbuf.mdd.ftp.length = ftp->len;
11862306a36Sopenharmony_ci	sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	len = strscpy(sccb->evbuf.mdd.ftp.fident, ftp->fname,
12162306a36Sopenharmony_ci		      HMCDRV_FTP_FIDENT_MAX);
12262306a36Sopenharmony_ci	if (len < 0) {
12362306a36Sopenharmony_ci		rc = -EINVAL;
12462306a36Sopenharmony_ci		goto out_free;
12562306a36Sopenharmony_ci	}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	req->command = SCLP_CMDW_WRITE_EVENT_DATA;
12862306a36Sopenharmony_ci	req->sccb = sccb;
12962306a36Sopenharmony_ci	req->status = SCLP_REQ_FILLED;
13062306a36Sopenharmony_ci	req->callback = sclp_ftp_txcb;
13162306a36Sopenharmony_ci	req->callback_data = &completion;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	init_completion(&completion);
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	rc = sclp_add_request(req);
13662306a36Sopenharmony_ci	if (rc)
13762306a36Sopenharmony_ci		goto out_free;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* Wait for end of ftp sclp command. */
14062306a36Sopenharmony_ci	wait_for_completion(&completion);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#ifdef DEBUG
14362306a36Sopenharmony_ci	pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n",
14462306a36Sopenharmony_ci		 sccb->hdr.response_code, sccb->evbuf.hdr.flags);
14562306a36Sopenharmony_ci#endif
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/*
14862306a36Sopenharmony_ci	 * Check if sclp accepted the request. The data transfer runs
14962306a36Sopenharmony_ci	 * asynchronously and the completion is indicated with an
15062306a36Sopenharmony_ci	 * sclp ET7 event.
15162306a36Sopenharmony_ci	 */
15262306a36Sopenharmony_ci	if (req->status != SCLP_REQ_DONE ||
15362306a36Sopenharmony_ci	    (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */
15462306a36Sopenharmony_ci	    (sccb->hdr.response_code & 0xffU) != 0x20U) {
15562306a36Sopenharmony_ci		rc = -EIO;
15662306a36Sopenharmony_ci	}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciout_free:
15962306a36Sopenharmony_ci	free_page((unsigned long) sccb);
16062306a36Sopenharmony_ci	kfree(req);
16162306a36Sopenharmony_ci	return rc;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci/**
16562306a36Sopenharmony_ci * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command
16662306a36Sopenharmony_ci * @ftp: pointer to FTP command specification
16762306a36Sopenharmony_ci * @fsize: return of file size (or NULL if undesirable)
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci * Attention: Notice that this function is not reentrant - so the caller
17062306a36Sopenharmony_ci * must ensure locking.
17162306a36Sopenharmony_ci *
17262306a36Sopenharmony_ci * Return: number of bytes read/written or a (negative) error code
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_cissize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	ssize_t len;
17762306a36Sopenharmony_ci#ifdef DEBUG
17862306a36Sopenharmony_ci	unsigned long start_jiffies;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n",
18162306a36Sopenharmony_ci		 ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len);
18262306a36Sopenharmony_ci	start_jiffies = jiffies;
18362306a36Sopenharmony_ci#endif
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	init_completion(&sclp_ftp_rx_complete);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	/* Start ftp sclp command. */
18862306a36Sopenharmony_ci	len = sclp_ftp_et7(ftp);
18962306a36Sopenharmony_ci	if (len)
19062306a36Sopenharmony_ci		goto out_unlock;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	/*
19362306a36Sopenharmony_ci	 * There is no way to cancel the sclp ET7 request, the code
19462306a36Sopenharmony_ci	 * needs to wait unconditionally until the transfer is complete.
19562306a36Sopenharmony_ci	 */
19662306a36Sopenharmony_ci	wait_for_completion(&sclp_ftp_rx_complete);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci#ifdef DEBUG
19962306a36Sopenharmony_ci	pr_debug("completed SCLP (ET7) request after %lu ms (all)\n",
20062306a36Sopenharmony_ci		 (jiffies - start_jiffies) * 1000 / HZ);
20162306a36Sopenharmony_ci	pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n",
20262306a36Sopenharmony_ci		 sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize);
20362306a36Sopenharmony_ci#endif
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	switch (sclp_ftp_ldflg) {
20662306a36Sopenharmony_ci	case SCLP_DIAG_FTP_OK:
20762306a36Sopenharmony_ci		len = sclp_ftp_length;
20862306a36Sopenharmony_ci		if (fsize)
20962306a36Sopenharmony_ci			*fsize = sclp_ftp_fsize;
21062306a36Sopenharmony_ci		break;
21162306a36Sopenharmony_ci	case SCLP_DIAG_FTP_LDNPERM:
21262306a36Sopenharmony_ci		len = -EPERM;
21362306a36Sopenharmony_ci		break;
21462306a36Sopenharmony_ci	case SCLP_DIAG_FTP_LDRUNS:
21562306a36Sopenharmony_ci		len = -EBUSY;
21662306a36Sopenharmony_ci		break;
21762306a36Sopenharmony_ci	case SCLP_DIAG_FTP_LDFAIL:
21862306a36Sopenharmony_ci		len = -ENOENT;
21962306a36Sopenharmony_ci		break;
22062306a36Sopenharmony_ci	default:
22162306a36Sopenharmony_ci		len = -EIO;
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ciout_unlock:
22662306a36Sopenharmony_ci	return len;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci/*
23062306a36Sopenharmony_ci * ET7 event listener
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_cistatic struct sclp_register sclp_ftp_event = {
23362306a36Sopenharmony_ci	.send_mask = EVTYP_DIAG_TEST_MASK,    /* want tx events */
23462306a36Sopenharmony_ci	.receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */
23562306a36Sopenharmony_ci	.receiver_fn = sclp_ftp_rxcb,	      /* async callback (rx) */
23662306a36Sopenharmony_ci	.state_change_fn = NULL,
23762306a36Sopenharmony_ci};
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/**
24062306a36Sopenharmony_ci * sclp_ftp_startup() - startup of FTP services, when running on LPAR
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_ciint sclp_ftp_startup(void)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci#ifdef DEBUG
24562306a36Sopenharmony_ci	unsigned long info;
24662306a36Sopenharmony_ci#endif
24762306a36Sopenharmony_ci	int rc;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	rc = sclp_register(&sclp_ftp_event);
25062306a36Sopenharmony_ci	if (rc)
25162306a36Sopenharmony_ci		return rc;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci#ifdef DEBUG
25462306a36Sopenharmony_ci	info = get_zeroed_page(GFP_KERNEL);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (info != 0) {
25762306a36Sopenharmony_ci		struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */
26062306a36Sopenharmony_ci			info222->name[sizeof(info222->name) - 1] = '\0';
26162306a36Sopenharmony_ci			EBCASC_500(info222->name, sizeof(info222->name) - 1);
26262306a36Sopenharmony_ci			pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n",
26362306a36Sopenharmony_ci				 info222->lpar_number, info222->name);
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		free_page(info);
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci#endif	/* DEBUG */
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/**
27362306a36Sopenharmony_ci * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR
27462306a36Sopenharmony_ci */
27562306a36Sopenharmony_civoid sclp_ftp_shutdown(void)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	sclp_unregister(&sclp_ftp_event);
27862306a36Sopenharmony_ci}
279