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