18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2013 68c2ecf20Sopenharmony_ci * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "hmcdrv" 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/mm.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/wait.h> 188c2ecf20Sopenharmony_ci#include <linux/string.h> 198c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 208c2ecf20Sopenharmony_ci#include <asm/sysinfo.h> 218c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "sclp.h" 248c2ecf20Sopenharmony_ci#include "sclp_diag.h" 258c2ecf20Sopenharmony_ci#include "sclp_ftp.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic DECLARE_COMPLETION(sclp_ftp_rx_complete); 288c2ecf20Sopenharmony_cistatic u8 sclp_ftp_ldflg; 298c2ecf20Sopenharmony_cistatic u64 sclp_ftp_fsize; 308c2ecf20Sopenharmony_cistatic u64 sclp_ftp_length; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/** 338c2ecf20Sopenharmony_ci * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistatic void sclp_ftp_txcb(struct sclp_req *req, void *data) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci struct completion *completion = data; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#ifdef DEBUG 408c2ecf20Sopenharmony_ci pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n", 418c2ecf20Sopenharmony_ci req->sccb, 24, req->sccb); 428c2ecf20Sopenharmony_ci#endif 438c2ecf20Sopenharmony_ci complete(completion); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/** 478c2ecf20Sopenharmony_ci * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback 488c2ecf20Sopenharmony_ci */ 498c2ecf20Sopenharmony_cistatic void sclp_ftp_rxcb(struct evbuf_header *evbuf) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * Check for Diagnostic Test FTP Service 558c2ecf20Sopenharmony_ci */ 568c2ecf20Sopenharmony_ci if (evbuf->type != EVTYP_DIAG_TEST || 578c2ecf20Sopenharmony_ci diag->route != SCLP_DIAG_FTP_ROUTE || 588c2ecf20Sopenharmony_ci diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX || 598c2ecf20Sopenharmony_ci evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN) 608c2ecf20Sopenharmony_ci return; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#ifdef DEBUG 638c2ecf20Sopenharmony_ci pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n", 648c2ecf20Sopenharmony_ci evbuf, 24, evbuf); 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* 688c2ecf20Sopenharmony_ci * Because the event buffer is located in a page which is owned 698c2ecf20Sopenharmony_ci * by the SCLP core, all data of interest must be copied. The 708c2ecf20Sopenharmony_ci * error indication is in 'sclp_ftp_ldflg' 718c2ecf20Sopenharmony_ci */ 728c2ecf20Sopenharmony_ci sclp_ftp_ldflg = diag->mdd.ftp.ldflg; 738c2ecf20Sopenharmony_ci sclp_ftp_fsize = diag->mdd.ftp.fsize; 748c2ecf20Sopenharmony_ci sclp_ftp_length = diag->mdd.ftp.length; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci complete(&sclp_ftp_rx_complete); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/** 808c2ecf20Sopenharmony_ci * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request 818c2ecf20Sopenharmony_ci * @ftp: pointer to FTP descriptor 828c2ecf20Sopenharmony_ci * 838c2ecf20Sopenharmony_ci * Return: 0 on success, else a (negative) error code 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_cistatic int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct completion completion; 888c2ecf20Sopenharmony_ci struct sclp_diag_sccb *sccb; 898c2ecf20Sopenharmony_ci struct sclp_req *req; 908c2ecf20Sopenharmony_ci size_t len; 918c2ecf20Sopenharmony_ci int rc; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci req = kzalloc(sizeof(*req), GFP_KERNEL); 948c2ecf20Sopenharmony_ci sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 958c2ecf20Sopenharmony_ci if (!req || !sccb) { 968c2ecf20Sopenharmony_ci rc = -ENOMEM; 978c2ecf20Sopenharmony_ci goto out_free; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN + 1018c2ecf20Sopenharmony_ci sizeof(struct sccb_header); 1028c2ecf20Sopenharmony_ci sccb->evbuf.hdr.type = EVTYP_DIAG_TEST; 1038c2ecf20Sopenharmony_ci sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN; 1048c2ecf20Sopenharmony_ci sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */ 1058c2ecf20Sopenharmony_ci sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE; 1068c2ecf20Sopenharmony_ci sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX; 1078c2ecf20Sopenharmony_ci sccb->evbuf.mdd.ftp.srcflg = 0; 1088c2ecf20Sopenharmony_ci sccb->evbuf.mdd.ftp.pgsize = 0; 1098c2ecf20Sopenharmony_ci sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE; 1108c2ecf20Sopenharmony_ci sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL; 1118c2ecf20Sopenharmony_ci sccb->evbuf.mdd.ftp.fsize = 0; 1128c2ecf20Sopenharmony_ci sccb->evbuf.mdd.ftp.cmd = ftp->id; 1138c2ecf20Sopenharmony_ci sccb->evbuf.mdd.ftp.offset = ftp->ofs; 1148c2ecf20Sopenharmony_ci sccb->evbuf.mdd.ftp.length = ftp->len; 1158c2ecf20Sopenharmony_ci sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname, 1188c2ecf20Sopenharmony_ci HMCDRV_FTP_FIDENT_MAX); 1198c2ecf20Sopenharmony_ci if (len >= HMCDRV_FTP_FIDENT_MAX) { 1208c2ecf20Sopenharmony_ci rc = -EINVAL; 1218c2ecf20Sopenharmony_ci goto out_free; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci req->command = SCLP_CMDW_WRITE_EVENT_DATA; 1258c2ecf20Sopenharmony_ci req->sccb = sccb; 1268c2ecf20Sopenharmony_ci req->status = SCLP_REQ_FILLED; 1278c2ecf20Sopenharmony_ci req->callback = sclp_ftp_txcb; 1288c2ecf20Sopenharmony_ci req->callback_data = &completion; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci init_completion(&completion); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci rc = sclp_add_request(req); 1338c2ecf20Sopenharmony_ci if (rc) 1348c2ecf20Sopenharmony_ci goto out_free; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Wait for end of ftp sclp command. */ 1378c2ecf20Sopenharmony_ci wait_for_completion(&completion); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci#ifdef DEBUG 1408c2ecf20Sopenharmony_ci pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n", 1418c2ecf20Sopenharmony_ci sccb->hdr.response_code, sccb->evbuf.hdr.flags); 1428c2ecf20Sopenharmony_ci#endif 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* 1458c2ecf20Sopenharmony_ci * Check if sclp accepted the request. The data transfer runs 1468c2ecf20Sopenharmony_ci * asynchronously and the completion is indicated with an 1478c2ecf20Sopenharmony_ci * sclp ET7 event. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_ci if (req->status != SCLP_REQ_DONE || 1508c2ecf20Sopenharmony_ci (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */ 1518c2ecf20Sopenharmony_ci (sccb->hdr.response_code & 0xffU) != 0x20U) { 1528c2ecf20Sopenharmony_ci rc = -EIO; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciout_free: 1568c2ecf20Sopenharmony_ci free_page((unsigned long) sccb); 1578c2ecf20Sopenharmony_ci kfree(req); 1588c2ecf20Sopenharmony_ci return rc; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/** 1628c2ecf20Sopenharmony_ci * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command 1638c2ecf20Sopenharmony_ci * @ftp: pointer to FTP command specification 1648c2ecf20Sopenharmony_ci * @fsize: return of file size (or NULL if undesirable) 1658c2ecf20Sopenharmony_ci * 1668c2ecf20Sopenharmony_ci * Attention: Notice that this function is not reentrant - so the caller 1678c2ecf20Sopenharmony_ci * must ensure locking. 1688c2ecf20Sopenharmony_ci * 1698c2ecf20Sopenharmony_ci * Return: number of bytes read/written or a (negative) error code 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_cissize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci ssize_t len; 1748c2ecf20Sopenharmony_ci#ifdef DEBUG 1758c2ecf20Sopenharmony_ci unsigned long start_jiffies; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n", 1788c2ecf20Sopenharmony_ci ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); 1798c2ecf20Sopenharmony_ci start_jiffies = jiffies; 1808c2ecf20Sopenharmony_ci#endif 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci init_completion(&sclp_ftp_rx_complete); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Start ftp sclp command. */ 1858c2ecf20Sopenharmony_ci len = sclp_ftp_et7(ftp); 1868c2ecf20Sopenharmony_ci if (len) 1878c2ecf20Sopenharmony_ci goto out_unlock; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * There is no way to cancel the sclp ET7 request, the code 1918c2ecf20Sopenharmony_ci * needs to wait unconditionally until the transfer is complete. 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci wait_for_completion(&sclp_ftp_rx_complete); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci#ifdef DEBUG 1968c2ecf20Sopenharmony_ci pr_debug("completed SCLP (ET7) request after %lu ms (all)\n", 1978c2ecf20Sopenharmony_ci (jiffies - start_jiffies) * 1000 / HZ); 1988c2ecf20Sopenharmony_ci pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n", 1998c2ecf20Sopenharmony_ci sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize); 2008c2ecf20Sopenharmony_ci#endif 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci switch (sclp_ftp_ldflg) { 2038c2ecf20Sopenharmony_ci case SCLP_DIAG_FTP_OK: 2048c2ecf20Sopenharmony_ci len = sclp_ftp_length; 2058c2ecf20Sopenharmony_ci if (fsize) 2068c2ecf20Sopenharmony_ci *fsize = sclp_ftp_fsize; 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci case SCLP_DIAG_FTP_LDNPERM: 2098c2ecf20Sopenharmony_ci len = -EPERM; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci case SCLP_DIAG_FTP_LDRUNS: 2128c2ecf20Sopenharmony_ci len = -EBUSY; 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci case SCLP_DIAG_FTP_LDFAIL: 2158c2ecf20Sopenharmony_ci len = -ENOENT; 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci default: 2188c2ecf20Sopenharmony_ci len = -EIO; 2198c2ecf20Sopenharmony_ci break; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ciout_unlock: 2238c2ecf20Sopenharmony_ci return len; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci/* 2278c2ecf20Sopenharmony_ci * ET7 event listener 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic struct sclp_register sclp_ftp_event = { 2308c2ecf20Sopenharmony_ci .send_mask = EVTYP_DIAG_TEST_MASK, /* want tx events */ 2318c2ecf20Sopenharmony_ci .receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */ 2328c2ecf20Sopenharmony_ci .receiver_fn = sclp_ftp_rxcb, /* async callback (rx) */ 2338c2ecf20Sopenharmony_ci .state_change_fn = NULL, 2348c2ecf20Sopenharmony_ci .pm_event_fn = NULL, 2358c2ecf20Sopenharmony_ci}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/** 2388c2ecf20Sopenharmony_ci * sclp_ftp_startup() - startup of FTP services, when running on LPAR 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ciint sclp_ftp_startup(void) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci#ifdef DEBUG 2438c2ecf20Sopenharmony_ci unsigned long info; 2448c2ecf20Sopenharmony_ci#endif 2458c2ecf20Sopenharmony_ci int rc; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci rc = sclp_register(&sclp_ftp_event); 2488c2ecf20Sopenharmony_ci if (rc) 2498c2ecf20Sopenharmony_ci return rc; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci#ifdef DEBUG 2528c2ecf20Sopenharmony_ci info = get_zeroed_page(GFP_KERNEL); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (info != 0) { 2558c2ecf20Sopenharmony_ci struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */ 2588c2ecf20Sopenharmony_ci info222->name[sizeof(info222->name) - 1] = '\0'; 2598c2ecf20Sopenharmony_ci EBCASC_500(info222->name, sizeof(info222->name) - 1); 2608c2ecf20Sopenharmony_ci pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n", 2618c2ecf20Sopenharmony_ci info222->lpar_number, info222->name); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci free_page(info); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci#endif /* DEBUG */ 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/** 2718c2ecf20Sopenharmony_ci * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_civoid sclp_ftp_shutdown(void) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci sclp_unregister(&sclp_ftp_event); 2768c2ecf20Sopenharmony_ci} 277