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