Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *    DIAGNOSE X'2C4' instruction based HMC FTP services, useable on z/VM
0004  *
0005  *    Copyright IBM Corp. 2013
0006  *    Author(s): Ralf Hoppe (rhoppe@de.ibm.com)
0007  *
0008  */
0009 
0010 #define KMSG_COMPONENT "hmcdrv"
0011 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0012 
0013 #include <linux/kernel.h>
0014 #include <linux/mm.h>
0015 #include <linux/irq.h>
0016 #include <linux/wait.h>
0017 #include <linux/string.h>
0018 #include <asm/asm-extable.h>
0019 #include <asm/ctl_reg.h>
0020 #include <asm/diag.h>
0021 
0022 #include "hmcdrv_ftp.h"
0023 #include "diag_ftp.h"
0024 
0025 /* DIAGNOSE X'2C4' return codes in Ry */
0026 #define DIAG_FTP_RET_OK 0 /* HMC FTP started successfully */
0027 #define DIAG_FTP_RET_EBUSY  4 /* HMC FTP service currently busy */
0028 #define DIAG_FTP_RET_EIO    8 /* HMC FTP service I/O error */
0029 /* and an artificial extension */
0030 #define DIAG_FTP_RET_EPERM  2 /* HMC FTP service privilege error */
0031 
0032 /* FTP service status codes (after INTR at guest real location 133) */
0033 #define DIAG_FTP_STAT_OK    0U /* request completed successfully */
0034 #define DIAG_FTP_STAT_PGCC  4U /* program check condition */
0035 #define DIAG_FTP_STAT_PGIOE 8U /* paging I/O error */
0036 #define DIAG_FTP_STAT_TIMEOUT   12U /* timeout */
0037 #define DIAG_FTP_STAT_EBASE 16U /* base of error codes from SCLP */
0038 #define DIAG_FTP_STAT_LDFAIL    (DIAG_FTP_STAT_EBASE + 1U) /* failed */
0039 #define DIAG_FTP_STAT_LDNPERM   (DIAG_FTP_STAT_EBASE + 2U) /* not allowed */
0040 #define DIAG_FTP_STAT_LDRUNS    (DIAG_FTP_STAT_EBASE + 3U) /* runs */
0041 #define DIAG_FTP_STAT_LDNRUNS   (DIAG_FTP_STAT_EBASE + 4U) /* not runs */
0042 
0043 /**
0044  * struct diag_ftp_ldfpl - load file FTP parameter list (LDFPL)
0045  * @bufaddr: real buffer address (at 4k boundary)
0046  * @buflen: length of buffer
0047  * @offset: dir/file offset
0048  * @intparm: interruption parameter (unused)
0049  * @transferred: bytes transferred
0050  * @fsize: file size, filled on GET
0051  * @failaddr: failing address
0052  * @spare: padding
0053  * @fident: file name - ASCII
0054  */
0055 struct diag_ftp_ldfpl {
0056     u64 bufaddr;
0057     u64 buflen;
0058     u64 offset;
0059     u64 intparm;
0060     u64 transferred;
0061     u64 fsize;
0062     u64 failaddr;
0063     u64 spare;
0064     u8 fident[HMCDRV_FTP_FIDENT_MAX];
0065 } __packed;
0066 
0067 static DECLARE_COMPLETION(diag_ftp_rx_complete);
0068 static int diag_ftp_subcode;
0069 
0070 /**
0071  * diag_ftp_handler() - FTP services IRQ handler
0072  * @extirq: external interrupt (sub-) code
0073  * @param32: 32-bit interruption parameter from &struct diag_ftp_ldfpl
0074  * @param64: unused (for 64-bit interrupt parameters)
0075  */
0076 static void diag_ftp_handler(struct ext_code extirq,
0077                  unsigned int param32,
0078                  unsigned long param64)
0079 {
0080     if ((extirq.subcode >> 8) != 8)
0081         return; /* not a FTP services sub-code */
0082 
0083     inc_irq_stat(IRQEXT_FTP);
0084     diag_ftp_subcode = extirq.subcode & 0xffU;
0085     complete(&diag_ftp_rx_complete);
0086 }
0087 
0088 /**
0089  * diag_ftp_2c4() - DIAGNOSE X'2C4' service call
0090  * @fpl: pointer to prepared LDFPL
0091  * @cmd: FTP command to be executed
0092  *
0093  * Performs a DIAGNOSE X'2C4' call with (input/output) FTP parameter list
0094  * @fpl and FTP function code @cmd. In case of an error the function does
0095  * nothing and returns an (negative) error code.
0096  *
0097  * Notes:
0098  * 1. This function only initiates a transfer, so the caller must wait
0099  *    for completion (asynchronous execution).
0100  * 2. The FTP parameter list @fpl must be aligned to a double-word boundary.
0101  * 3. fpl->bufaddr must be a real address, 4k aligned
0102  */
0103 static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl,
0104             enum hmcdrv_ftp_cmdid cmd)
0105 {
0106     int rc;
0107 
0108     diag_stat_inc(DIAG_STAT_X2C4);
0109     asm volatile(
0110         "   diag    %[addr],%[cmd],0x2c4\n"
0111         "0: j   2f\n"
0112         "1: la  %[rc],%[err]\n"
0113         "2:\n"
0114         EX_TABLE(0b, 1b)
0115         : [rc] "=d" (rc), "+m" (*fpl)
0116         : [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)),
0117           [err] "i" (DIAG_FTP_RET_EPERM)
0118         : "cc");
0119 
0120     switch (rc) {
0121     case DIAG_FTP_RET_OK:
0122         return 0;
0123     case DIAG_FTP_RET_EBUSY:
0124         return -EBUSY;
0125     case DIAG_FTP_RET_EPERM:
0126         return -EPERM;
0127     case DIAG_FTP_RET_EIO:
0128     default:
0129         return -EIO;
0130     }
0131 }
0132 
0133 /**
0134  * diag_ftp_cmd() - executes a DIAG X'2C4' FTP command, targeting a HMC
0135  * @ftp: pointer to FTP command specification
0136  * @fsize: return of file size (or NULL if undesirable)
0137  *
0138  * Attention: Notice that this function is not reentrant - so the caller
0139  * must ensure locking.
0140  *
0141  * Return: number of bytes read/written or a (negative) error code
0142  */
0143 ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
0144 {
0145     struct diag_ftp_ldfpl *ldfpl;
0146     ssize_t len;
0147 #ifdef DEBUG
0148     unsigned long start_jiffies;
0149 
0150     pr_debug("starting DIAG X'2C4' on '%s', requesting %zd bytes\n",
0151          ftp->fname, ftp->len);
0152     start_jiffies = jiffies;
0153 #endif
0154     init_completion(&diag_ftp_rx_complete);
0155 
0156     ldfpl = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
0157     if (!ldfpl) {
0158         len = -ENOMEM;
0159         goto out;
0160     }
0161 
0162     len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident));
0163     if (len >= HMCDRV_FTP_FIDENT_MAX) {
0164         len = -EINVAL;
0165         goto out_free;
0166     }
0167 
0168     ldfpl->transferred = 0;
0169     ldfpl->fsize = 0;
0170     ldfpl->offset = ftp->ofs;
0171     ldfpl->buflen = ftp->len;
0172     ldfpl->bufaddr = virt_to_phys(ftp->buf);
0173 
0174     len = diag_ftp_2c4(ldfpl, ftp->id);
0175     if (len)
0176         goto out_free;
0177 
0178     /*
0179      * There is no way to cancel the running diag X'2C4', the code
0180      * needs to wait unconditionally until the transfer is complete.
0181      */
0182     wait_for_completion(&diag_ftp_rx_complete);
0183 
0184 #ifdef DEBUG
0185     pr_debug("completed DIAG X'2C4' after %lu ms\n",
0186          (jiffies - start_jiffies) * 1000 / HZ);
0187     pr_debug("status of DIAG X'2C4' is %u, with %lld/%lld bytes\n",
0188          diag_ftp_subcode, ldfpl->transferred, ldfpl->fsize);
0189 #endif
0190 
0191     switch (diag_ftp_subcode) {
0192     case DIAG_FTP_STAT_OK: /* success */
0193         len = ldfpl->transferred;
0194         if (fsize)
0195             *fsize = ldfpl->fsize;
0196         break;
0197     case DIAG_FTP_STAT_LDNPERM:
0198         len = -EPERM;
0199         break;
0200     case DIAG_FTP_STAT_LDRUNS:
0201         len = -EBUSY;
0202         break;
0203     case DIAG_FTP_STAT_LDFAIL:
0204         len = -ENOENT; /* no such file or media */
0205         break;
0206     default:
0207         len = -EIO;
0208         break;
0209     }
0210 
0211 out_free:
0212     free_page((unsigned long) ldfpl);
0213 out:
0214     return len;
0215 }
0216 
0217 /**
0218  * diag_ftp_startup() - startup of FTP services, when running on z/VM
0219  *
0220  * Return: 0 on success, else an (negative) error code
0221  */
0222 int diag_ftp_startup(void)
0223 {
0224     int rc;
0225 
0226     rc = register_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
0227     if (rc)
0228         return rc;
0229 
0230     irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL);
0231     return 0;
0232 }
0233 
0234 /**
0235  * diag_ftp_shutdown() - shutdown of FTP services, when running on z/VM
0236  */
0237 void diag_ftp_shutdown(void)
0238 {
0239     irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL);
0240     unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler);
0241 }