sclp_ftp.c (6908B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR 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/slab.h> 16#include <linux/io.h> 17#include <linux/wait.h> 18#include <linux/string.h> 19#include <linux/jiffies.h> 20#include <asm/sysinfo.h> 21#include <asm/ebcdic.h> 22 23#include "sclp.h" 24#include "sclp_diag.h" 25#include "sclp_ftp.h" 26 27static DECLARE_COMPLETION(sclp_ftp_rx_complete); 28static u8 sclp_ftp_ldflg; 29static u64 sclp_ftp_fsize; 30static u64 sclp_ftp_length; 31 32/** 33 * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback 34 * @req: sclp request 35 * @data: pointer to struct completion 36 */ 37static void sclp_ftp_txcb(struct sclp_req *req, void *data) 38{ 39 struct completion *completion = data; 40 41#ifdef DEBUG 42 pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n", 43 req->sccb, 24, req->sccb); 44#endif 45 complete(completion); 46} 47 48/** 49 * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback 50 * @evbuf: pointer to Diagnostic Test (ET7) event buffer 51 */ 52static void sclp_ftp_rxcb(struct evbuf_header *evbuf) 53{ 54 struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf; 55 56 /* 57 * Check for Diagnostic Test FTP Service 58 */ 59 if (evbuf->type != EVTYP_DIAG_TEST || 60 diag->route != SCLP_DIAG_FTP_ROUTE || 61 diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX || 62 evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN) 63 return; 64 65#ifdef DEBUG 66 pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n", 67 evbuf, 24, evbuf); 68#endif 69 70 /* 71 * Because the event buffer is located in a page which is owned 72 * by the SCLP core, all data of interest must be copied. The 73 * error indication is in 'sclp_ftp_ldflg' 74 */ 75 sclp_ftp_ldflg = diag->mdd.ftp.ldflg; 76 sclp_ftp_fsize = diag->mdd.ftp.fsize; 77 sclp_ftp_length = diag->mdd.ftp.length; 78 79 complete(&sclp_ftp_rx_complete); 80} 81 82/** 83 * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request 84 * @ftp: pointer to FTP descriptor 85 * 86 * Return: 0 on success, else a (negative) error code 87 */ 88static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp) 89{ 90 struct completion completion; 91 struct sclp_diag_sccb *sccb; 92 struct sclp_req *req; 93 size_t len; 94 int rc; 95 96 req = kzalloc(sizeof(*req), GFP_KERNEL); 97 sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 98 if (!req || !sccb) { 99 rc = -ENOMEM; 100 goto out_free; 101 } 102 103 sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN + 104 sizeof(struct sccb_header); 105 sccb->evbuf.hdr.type = EVTYP_DIAG_TEST; 106 sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN; 107 sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */ 108 sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE; 109 sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX; 110 sccb->evbuf.mdd.ftp.srcflg = 0; 111 sccb->evbuf.mdd.ftp.pgsize = 0; 112 sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE; 113 sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL; 114 sccb->evbuf.mdd.ftp.fsize = 0; 115 sccb->evbuf.mdd.ftp.cmd = ftp->id; 116 sccb->evbuf.mdd.ftp.offset = ftp->ofs; 117 sccb->evbuf.mdd.ftp.length = ftp->len; 118 sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf); 119 120 len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname, 121 HMCDRV_FTP_FIDENT_MAX); 122 if (len >= HMCDRV_FTP_FIDENT_MAX) { 123 rc = -EINVAL; 124 goto out_free; 125 } 126 127 req->command = SCLP_CMDW_WRITE_EVENT_DATA; 128 req->sccb = sccb; 129 req->status = SCLP_REQ_FILLED; 130 req->callback = sclp_ftp_txcb; 131 req->callback_data = &completion; 132 133 init_completion(&completion); 134 135 rc = sclp_add_request(req); 136 if (rc) 137 goto out_free; 138 139 /* Wait for end of ftp sclp command. */ 140 wait_for_completion(&completion); 141 142#ifdef DEBUG 143 pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n", 144 sccb->hdr.response_code, sccb->evbuf.hdr.flags); 145#endif 146 147 /* 148 * Check if sclp accepted the request. The data transfer runs 149 * asynchronously and the completion is indicated with an 150 * sclp ET7 event. 151 */ 152 if (req->status != SCLP_REQ_DONE || 153 (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */ 154 (sccb->hdr.response_code & 0xffU) != 0x20U) { 155 rc = -EIO; 156 } 157 158out_free: 159 free_page((unsigned long) sccb); 160 kfree(req); 161 return rc; 162} 163 164/** 165 * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command 166 * @ftp: pointer to FTP command specification 167 * @fsize: return of file size (or NULL if undesirable) 168 * 169 * Attention: Notice that this function is not reentrant - so the caller 170 * must ensure locking. 171 * 172 * Return: number of bytes read/written or a (negative) error code 173 */ 174ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) 175{ 176 ssize_t len; 177#ifdef DEBUG 178 unsigned long start_jiffies; 179 180 pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n", 181 ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); 182 start_jiffies = jiffies; 183#endif 184 185 init_completion(&sclp_ftp_rx_complete); 186 187 /* Start ftp sclp command. */ 188 len = sclp_ftp_et7(ftp); 189 if (len) 190 goto out_unlock; 191 192 /* 193 * There is no way to cancel the sclp ET7 request, the code 194 * needs to wait unconditionally until the transfer is complete. 195 */ 196 wait_for_completion(&sclp_ftp_rx_complete); 197 198#ifdef DEBUG 199 pr_debug("completed SCLP (ET7) request after %lu ms (all)\n", 200 (jiffies - start_jiffies) * 1000 / HZ); 201 pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n", 202 sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize); 203#endif 204 205 switch (sclp_ftp_ldflg) { 206 case SCLP_DIAG_FTP_OK: 207 len = sclp_ftp_length; 208 if (fsize) 209 *fsize = sclp_ftp_fsize; 210 break; 211 case SCLP_DIAG_FTP_LDNPERM: 212 len = -EPERM; 213 break; 214 case SCLP_DIAG_FTP_LDRUNS: 215 len = -EBUSY; 216 break; 217 case SCLP_DIAG_FTP_LDFAIL: 218 len = -ENOENT; 219 break; 220 default: 221 len = -EIO; 222 break; 223 } 224 225out_unlock: 226 return len; 227} 228 229/* 230 * ET7 event listener 231 */ 232static struct sclp_register sclp_ftp_event = { 233 .send_mask = EVTYP_DIAG_TEST_MASK, /* want tx events */ 234 .receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */ 235 .receiver_fn = sclp_ftp_rxcb, /* async callback (rx) */ 236 .state_change_fn = NULL, 237}; 238 239/** 240 * sclp_ftp_startup() - startup of FTP services, when running on LPAR 241 */ 242int sclp_ftp_startup(void) 243{ 244#ifdef DEBUG 245 unsigned long info; 246#endif 247 int rc; 248 249 rc = sclp_register(&sclp_ftp_event); 250 if (rc) 251 return rc; 252 253#ifdef DEBUG 254 info = get_zeroed_page(GFP_KERNEL); 255 256 if (info != 0) { 257 struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; 258 259 if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */ 260 info222->name[sizeof(info222->name) - 1] = '\0'; 261 EBCASC_500(info222->name, sizeof(info222->name) - 1); 262 pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n", 263 info222->lpar_number, info222->name); 264 } 265 266 free_page(info); 267 } 268#endif /* DEBUG */ 269 return 0; 270} 271 272/** 273 * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR 274 */ 275void sclp_ftp_shutdown(void) 276{ 277 sclp_unregister(&sclp_ftp_event); 278}