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