162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2013 662306a36Sopenharmony_ci * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define KMSG_COMPONENT "hmcdrv" 1162306a36Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/wait.h> 1862306a36Sopenharmony_ci#include <linux/string.h> 1962306a36Sopenharmony_ci#include <linux/jiffies.h> 2062306a36Sopenharmony_ci#include <asm/sysinfo.h> 2162306a36Sopenharmony_ci#include <asm/ebcdic.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include "sclp.h" 2462306a36Sopenharmony_ci#include "sclp_diag.h" 2562306a36Sopenharmony_ci#include "sclp_ftp.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic DECLARE_COMPLETION(sclp_ftp_rx_complete); 2862306a36Sopenharmony_cistatic u8 sclp_ftp_ldflg; 2962306a36Sopenharmony_cistatic u64 sclp_ftp_fsize; 3062306a36Sopenharmony_cistatic u64 sclp_ftp_length; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/** 3362306a36Sopenharmony_ci * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback 3462306a36Sopenharmony_ci * @req: sclp request 3562306a36Sopenharmony_ci * @data: pointer to struct completion 3662306a36Sopenharmony_ci */ 3762306a36Sopenharmony_cistatic void sclp_ftp_txcb(struct sclp_req *req, void *data) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci struct completion *completion = data; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#ifdef DEBUG 4262306a36Sopenharmony_ci pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n", 4362306a36Sopenharmony_ci req->sccb, 24, req->sccb); 4462306a36Sopenharmony_ci#endif 4562306a36Sopenharmony_ci complete(completion); 4662306a36Sopenharmony_ci} 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/** 4962306a36Sopenharmony_ci * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback 5062306a36Sopenharmony_ci * @evbuf: pointer to Diagnostic Test (ET7) event buffer 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_cistatic void sclp_ftp_rxcb(struct evbuf_header *evbuf) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* 5762306a36Sopenharmony_ci * Check for Diagnostic Test FTP Service 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci if (evbuf->type != EVTYP_DIAG_TEST || 6062306a36Sopenharmony_ci diag->route != SCLP_DIAG_FTP_ROUTE || 6162306a36Sopenharmony_ci diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX || 6262306a36Sopenharmony_ci evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN) 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#ifdef DEBUG 6662306a36Sopenharmony_ci pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n", 6762306a36Sopenharmony_ci evbuf, 24, evbuf); 6862306a36Sopenharmony_ci#endif 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci /* 7162306a36Sopenharmony_ci * Because the event buffer is located in a page which is owned 7262306a36Sopenharmony_ci * by the SCLP core, all data of interest must be copied. The 7362306a36Sopenharmony_ci * error indication is in 'sclp_ftp_ldflg' 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci sclp_ftp_ldflg = diag->mdd.ftp.ldflg; 7662306a36Sopenharmony_ci sclp_ftp_fsize = diag->mdd.ftp.fsize; 7762306a36Sopenharmony_ci sclp_ftp_length = diag->mdd.ftp.length; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci complete(&sclp_ftp_rx_complete); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/** 8362306a36Sopenharmony_ci * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request 8462306a36Sopenharmony_ci * @ftp: pointer to FTP descriptor 8562306a36Sopenharmony_ci * 8662306a36Sopenharmony_ci * Return: 0 on success, else a (negative) error code 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct completion completion; 9162306a36Sopenharmony_ci struct sclp_diag_sccb *sccb; 9262306a36Sopenharmony_ci struct sclp_req *req; 9362306a36Sopenharmony_ci ssize_t len; 9462306a36Sopenharmony_ci int rc; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci req = kzalloc(sizeof(*req), GFP_KERNEL); 9762306a36Sopenharmony_ci sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 9862306a36Sopenharmony_ci if (!req || !sccb) { 9962306a36Sopenharmony_ci rc = -ENOMEM; 10062306a36Sopenharmony_ci goto out_free; 10162306a36Sopenharmony_ci } 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN + 10462306a36Sopenharmony_ci sizeof(struct sccb_header); 10562306a36Sopenharmony_ci sccb->evbuf.hdr.type = EVTYP_DIAG_TEST; 10662306a36Sopenharmony_ci sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN; 10762306a36Sopenharmony_ci sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */ 10862306a36Sopenharmony_ci sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE; 10962306a36Sopenharmony_ci sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX; 11062306a36Sopenharmony_ci sccb->evbuf.mdd.ftp.srcflg = 0; 11162306a36Sopenharmony_ci sccb->evbuf.mdd.ftp.pgsize = 0; 11262306a36Sopenharmony_ci sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE; 11362306a36Sopenharmony_ci sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL; 11462306a36Sopenharmony_ci sccb->evbuf.mdd.ftp.fsize = 0; 11562306a36Sopenharmony_ci sccb->evbuf.mdd.ftp.cmd = ftp->id; 11662306a36Sopenharmony_ci sccb->evbuf.mdd.ftp.offset = ftp->ofs; 11762306a36Sopenharmony_ci sccb->evbuf.mdd.ftp.length = ftp->len; 11862306a36Sopenharmony_ci sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci len = strscpy(sccb->evbuf.mdd.ftp.fident, ftp->fname, 12162306a36Sopenharmony_ci HMCDRV_FTP_FIDENT_MAX); 12262306a36Sopenharmony_ci if (len < 0) { 12362306a36Sopenharmony_ci rc = -EINVAL; 12462306a36Sopenharmony_ci goto out_free; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci req->command = SCLP_CMDW_WRITE_EVENT_DATA; 12862306a36Sopenharmony_ci req->sccb = sccb; 12962306a36Sopenharmony_ci req->status = SCLP_REQ_FILLED; 13062306a36Sopenharmony_ci req->callback = sclp_ftp_txcb; 13162306a36Sopenharmony_ci req->callback_data = &completion; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci init_completion(&completion); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci rc = sclp_add_request(req); 13662306a36Sopenharmony_ci if (rc) 13762306a36Sopenharmony_ci goto out_free; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* Wait for end of ftp sclp command. */ 14062306a36Sopenharmony_ci wait_for_completion(&completion); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#ifdef DEBUG 14362306a36Sopenharmony_ci pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n", 14462306a36Sopenharmony_ci sccb->hdr.response_code, sccb->evbuf.hdr.flags); 14562306a36Sopenharmony_ci#endif 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci /* 14862306a36Sopenharmony_ci * Check if sclp accepted the request. The data transfer runs 14962306a36Sopenharmony_ci * asynchronously and the completion is indicated with an 15062306a36Sopenharmony_ci * sclp ET7 event. 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci if (req->status != SCLP_REQ_DONE || 15362306a36Sopenharmony_ci (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */ 15462306a36Sopenharmony_ci (sccb->hdr.response_code & 0xffU) != 0x20U) { 15562306a36Sopenharmony_ci rc = -EIO; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ciout_free: 15962306a36Sopenharmony_ci free_page((unsigned long) sccb); 16062306a36Sopenharmony_ci kfree(req); 16162306a36Sopenharmony_ci return rc; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/** 16562306a36Sopenharmony_ci * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command 16662306a36Sopenharmony_ci * @ftp: pointer to FTP command specification 16762306a36Sopenharmony_ci * @fsize: return of file size (or NULL if undesirable) 16862306a36Sopenharmony_ci * 16962306a36Sopenharmony_ci * Attention: Notice that this function is not reentrant - so the caller 17062306a36Sopenharmony_ci * must ensure locking. 17162306a36Sopenharmony_ci * 17262306a36Sopenharmony_ci * Return: number of bytes read/written or a (negative) error code 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_cissize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci ssize_t len; 17762306a36Sopenharmony_ci#ifdef DEBUG 17862306a36Sopenharmony_ci unsigned long start_jiffies; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n", 18162306a36Sopenharmony_ci ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); 18262306a36Sopenharmony_ci start_jiffies = jiffies; 18362306a36Sopenharmony_ci#endif 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci init_completion(&sclp_ftp_rx_complete); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* Start ftp sclp command. */ 18862306a36Sopenharmony_ci len = sclp_ftp_et7(ftp); 18962306a36Sopenharmony_ci if (len) 19062306a36Sopenharmony_ci goto out_unlock; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* 19362306a36Sopenharmony_ci * There is no way to cancel the sclp ET7 request, the code 19462306a36Sopenharmony_ci * needs to wait unconditionally until the transfer is complete. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_ci wait_for_completion(&sclp_ftp_rx_complete); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci#ifdef DEBUG 19962306a36Sopenharmony_ci pr_debug("completed SCLP (ET7) request after %lu ms (all)\n", 20062306a36Sopenharmony_ci (jiffies - start_jiffies) * 1000 / HZ); 20162306a36Sopenharmony_ci pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n", 20262306a36Sopenharmony_ci sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize); 20362306a36Sopenharmony_ci#endif 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci switch (sclp_ftp_ldflg) { 20662306a36Sopenharmony_ci case SCLP_DIAG_FTP_OK: 20762306a36Sopenharmony_ci len = sclp_ftp_length; 20862306a36Sopenharmony_ci if (fsize) 20962306a36Sopenharmony_ci *fsize = sclp_ftp_fsize; 21062306a36Sopenharmony_ci break; 21162306a36Sopenharmony_ci case SCLP_DIAG_FTP_LDNPERM: 21262306a36Sopenharmony_ci len = -EPERM; 21362306a36Sopenharmony_ci break; 21462306a36Sopenharmony_ci case SCLP_DIAG_FTP_LDRUNS: 21562306a36Sopenharmony_ci len = -EBUSY; 21662306a36Sopenharmony_ci break; 21762306a36Sopenharmony_ci case SCLP_DIAG_FTP_LDFAIL: 21862306a36Sopenharmony_ci len = -ENOENT; 21962306a36Sopenharmony_ci break; 22062306a36Sopenharmony_ci default: 22162306a36Sopenharmony_ci len = -EIO; 22262306a36Sopenharmony_ci break; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ciout_unlock: 22662306a36Sopenharmony_ci return len; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* 23062306a36Sopenharmony_ci * ET7 event listener 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_cistatic struct sclp_register sclp_ftp_event = { 23362306a36Sopenharmony_ci .send_mask = EVTYP_DIAG_TEST_MASK, /* want tx events */ 23462306a36Sopenharmony_ci .receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */ 23562306a36Sopenharmony_ci .receiver_fn = sclp_ftp_rxcb, /* async callback (rx) */ 23662306a36Sopenharmony_ci .state_change_fn = NULL, 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/** 24062306a36Sopenharmony_ci * sclp_ftp_startup() - startup of FTP services, when running on LPAR 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ciint sclp_ftp_startup(void) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci#ifdef DEBUG 24562306a36Sopenharmony_ci unsigned long info; 24662306a36Sopenharmony_ci#endif 24762306a36Sopenharmony_ci int rc; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci rc = sclp_register(&sclp_ftp_event); 25062306a36Sopenharmony_ci if (rc) 25162306a36Sopenharmony_ci return rc; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci#ifdef DEBUG 25462306a36Sopenharmony_ci info = get_zeroed_page(GFP_KERNEL); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (info != 0) { 25762306a36Sopenharmony_ci struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */ 26062306a36Sopenharmony_ci info222->name[sizeof(info222->name) - 1] = '\0'; 26162306a36Sopenharmony_ci EBCASC_500(info222->name, sizeof(info222->name) - 1); 26262306a36Sopenharmony_ci pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n", 26362306a36Sopenharmony_ci info222->lpar_number, info222->name); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci free_page(info); 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci#endif /* DEBUG */ 26962306a36Sopenharmony_ci return 0; 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/** 27362306a36Sopenharmony_ci * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_civoid sclp_ftp_shutdown(void) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci sclp_unregister(&sclp_ftp_event); 27862306a36Sopenharmony_ci} 279