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