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 } 241