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