root/drivers/s390/char/diag_ftp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. diag_ftp_handler
  2. diag_ftp_2c4
  3. diag_ftp_cmd
  4. diag_ftp_startup
  5. diag_ftp_shutdown

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  *    DIAGNOSE X'2C4' instruction based HMC FTP services, useable on z/VM
   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/irq.h>
  16 #include <linux/wait.h>
  17 #include <linux/string.h>
  18 #include <asm/ctl_reg.h>
  19 #include <asm/diag.h>
  20 
  21 #include "hmcdrv_ftp.h"
  22 #include "diag_ftp.h"
  23 
  24 /* DIAGNOSE X'2C4' return codes in Ry */
  25 #define DIAG_FTP_RET_OK 0 /* HMC FTP started successfully */
  26 #define DIAG_FTP_RET_EBUSY      4 /* HMC FTP service currently busy */
  27 #define DIAG_FTP_RET_EIO        8 /* HMC FTP service I/O error */
  28 /* and an artificial extension */
  29 #define DIAG_FTP_RET_EPERM      2 /* HMC FTP service privilege error */
  30 
  31 /* FTP service status codes (after INTR at guest real location 133) */
  32 #define DIAG_FTP_STAT_OK        0U /* request completed successfully */
  33 #define DIAG_FTP_STAT_PGCC      4U /* program check condition */
  34 #define DIAG_FTP_STAT_PGIOE     8U /* paging I/O error */
  35 #define DIAG_FTP_STAT_TIMEOUT   12U /* timeout */
  36 #define DIAG_FTP_STAT_EBASE     16U /* base of error codes from SCLP */
  37 #define DIAG_FTP_STAT_LDFAIL    (DIAG_FTP_STAT_EBASE + 1U) /* failed */
  38 #define DIAG_FTP_STAT_LDNPERM   (DIAG_FTP_STAT_EBASE + 2U) /* not allowed */
  39 #define DIAG_FTP_STAT_LDRUNS    (DIAG_FTP_STAT_EBASE + 3U) /* runs */
  40 #define DIAG_FTP_STAT_LDNRUNS   (DIAG_FTP_STAT_EBASE + 4U) /* not runs */
  41 
  42 /**
  43  * struct diag_ftp_ldfpl - load file FTP parameter list (LDFPL)
  44  * @bufaddr: real buffer address (at 4k boundary)
  45  * @buflen: length of buffer
  46  * @offset: dir/file offset
  47  * @intparm: interruption parameter (unused)
  48  * @transferred: bytes transferred
  49  * @fsize: file size, filled on GET
  50  * @failaddr: failing address
  51  * @spare: padding
  52  * @fident: file name - ASCII
  53  */
  54 struct diag_ftp_ldfpl {
  55         u64 bufaddr;
  56         u64 buflen;
  57         u64 offset;
  58         u64 intparm;
  59         u64 transferred;
  60         u64 fsize;
  61         u64 failaddr;
  62         u64 spare;
  63         u8 fident[HMCDRV_FTP_FIDENT_MAX];
  64 } __packed;
  65 
  66 static DECLARE_COMPLETION(diag_ftp_rx_complete);
  67 static int diag_ftp_subcode;
  68 
  69 /**
  70  * diag_ftp_handler() - FTP services IRQ handler
  71  * @extirq: external interrupt (sub-) code
  72  * @param32: 32-bit interruption parameter from &struct diag_ftp_ldfpl
  73  * @param64: unused (for 64-bit interrupt parameters)
  74  */
  75 static void diag_ftp_handler(struct ext_code extirq,
  76                              unsigned int param32,
  77                              unsigned long param64)
  78 {
  79         if ((extirq.subcode >> 8) != 8)
  80                 return; /* not a FTP services sub-code */
  81 
  82         inc_irq_stat(IRQEXT_FTP);
  83         diag_ftp_subcode = extirq.subcode & 0xffU;
  84         complete(&diag_ftp_rx_complete);
  85 }
  86 
  87 /**
  88  * diag_ftp_2c4() - DIAGNOSE X'2C4' service call
  89  * @fpl: pointer to prepared LDFPL
  90  * @cmd: FTP command to be executed
  91  *
  92  * Performs a DIAGNOSE X'2C4' call with (input/output) FTP parameter list
  93  * @fpl and FTP function code @cmd. In case of an error the function does
  94  * nothing and returns an (negative) error code.
  95  *
  96  * Notes:
  97  * 1. This function only initiates a transfer, so the caller must wait
  98  *    for completion (asynchronous execution).
  99  * 2. The FTP parameter list @fpl must be aligned to a double-word boundary.
 100  * 3. fpl->bufaddr must be a real address, 4k aligned
 101  */
 102 static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl,
 103                         enum hmcdrv_ftp_cmdid cmd)
 104 {
 105         int rc;
 106 
 107         diag_stat_inc(DIAG_STAT_X2C4);
 108         asm volatile(
 109                 "       diag    %[addr],%[cmd],0x2c4\n"
 110                 "0:     j       2f\n"
 111                 "1:     la      %[rc],%[err]\n"
 112                 "2:\n"
 113                 EX_TABLE(0b, 1b)
 114                 : [rc] "=d" (rc), "+m" (*fpl)
 115                 : [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)),
 116                   [err] "i" (DIAG_FTP_RET_EPERM)
 117                 : "cc");
 118 
 119         switch (rc) {
 120         case DIAG_FTP_RET_OK:
 121                 return 0;
 122         case DIAG_FTP_RET_EBUSY:
 123                 return -EBUSY;
 124         case DIAG_FTP_RET_EPERM:
 125                 return -EPERM;
 126         case DIAG_FTP_RET_EIO:
 127         default:
 128                 return -EIO;
 129         }
 130 }
 131 
 132 /**
 133  * diag_ftp_cmd() - executes a DIAG X'2C4' FTP command, targeting a HMC
 134  * @ftp: pointer to FTP command specification
 135  * @fsize: return of file size (or NULL if undesirable)
 136  *
 137  * Attention: Notice that this function is not reentrant - so the caller
 138  * must ensure locking.
 139  *
 140  * Return: number of bytes read/written or a (negative) error code
 141  */
 142 ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
 143 {
 144         struct diag_ftp_ldfpl *ldfpl;
 145         ssize_t len;
 146 #ifdef DEBUG
 147         unsigned long start_jiffies;
 148 
 149         pr_debug("starting DIAG X'2C4' on '%s', requesting %zd bytes\n",
 150                  ftp->fname, ftp->len);
 151         start_jiffies = jiffies;
 152 #endif
 153         init_completion(&diag_ftp_rx_complete);
 154 
 155         ldfpl = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
 156         if (!ldfpl) {
 157                 len = -ENOMEM;
 158                 goto out;
 159         }
 160 
 161         len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident));
 162         if (len >= HMCDRV_FTP_FIDENT_MAX) {
 163                 len = -EINVAL;
 164                 goto out_free;
 165         }
 166 
 167         ldfpl->transferred = 0;
 168         ldfpl->fsize = 0;
 169         ldfpl->offset = ftp->ofs;
 170         ldfpl->buflen = ftp->len;
 171         ldfpl->bufaddr = virt_to_phys(ftp->buf);
 172 
 173         len = diag_ftp_2c4(ldfpl, ftp->id);
 174         if (len)
 175                 goto out_free;
 176 
 177         /*
 178          * There is no way to cancel the running diag X'2C4', the code
 179          * needs to wait unconditionally until the transfer is complete.
 180          */
 181         wait_for_completion(&diag_ftp_rx_complete);
 182 
 183 #ifdef DEBUG
 184         pr_debug("completed DIAG X'2C4' after %lu ms\n",
 185                  (jiffies - start_jiffies) * 1000 / HZ);
 186         pr_debug("status of DIAG X'2C4' is %u, with %lld/%lld bytes\n",
 187                  diag_ftp_subcode, ldfpl->transferred, ldfpl->fsize);
 188 #endif
 189 
 190         switch (diag_ftp_subcode) {
 191         case DIAG_FTP_STAT_OK: /* success */
 192                 len = ldfpl->transferred;
 193                 if (fsize)
 194                         *fsize = ldfpl->fsize;
 195                 break;
 196         case DIAG_FTP_STAT_LDNPERM:
 197                 len = -EPERM;
 198                 break;
 199         case DIAG_FTP_STAT_LDRUNS:
 200                 len = -EBUSY;
 201                 break;
 202         case DIAG_FTP_STAT_LDFAIL:
 203                 len = -ENOENT; /* no such file or media */
 204                 break;
 205         default:
 206                 len = -EIO;
 207                 break;
 208         }
 209 
 210 out_free:
 211         free_page((unsigned long) ldfpl);
 212 out:
 213         return len;
 214 }
 215 
 216 /**
 217  * diag_ftp_startup() - startup of FTP services, when running on z/VM
 218  *
 219  * Return: 0 on success, else an (negative) error code
 220  */
 221 int diag_ftp_startup(void)
 222 {
 223         int rc;
 224 
 225         rc = register_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
 226         if (rc)
 227                 return rc;
 228 
 229         irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL);
 230         return 0;
 231 }
 232 
 233 /**
 234  * diag_ftp_shutdown() - shutdown of FTP services, when running on z/VM
 235  */
 236 void diag_ftp_shutdown(void)
 237 {
 238         irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL);
 239         unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
 240 }

/* [<][>][^][v][top][bottom][index][help] */