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