cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sclp_ftp.c (6908B)


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