18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * DIAGNOSE X'2C4' instruction based HMC FTP services, useable on z/VM 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/irq.h> 168c2ecf20Sopenharmony_ci#include <linux/wait.h> 178c2ecf20Sopenharmony_ci#include <linux/string.h> 188c2ecf20Sopenharmony_ci#include <asm/ctl_reg.h> 198c2ecf20Sopenharmony_ci#include <asm/diag.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "hmcdrv_ftp.h" 228c2ecf20Sopenharmony_ci#include "diag_ftp.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* DIAGNOSE X'2C4' return codes in Ry */ 258c2ecf20Sopenharmony_ci#define DIAG_FTP_RET_OK 0 /* HMC FTP started successfully */ 268c2ecf20Sopenharmony_ci#define DIAG_FTP_RET_EBUSY 4 /* HMC FTP service currently busy */ 278c2ecf20Sopenharmony_ci#define DIAG_FTP_RET_EIO 8 /* HMC FTP service I/O error */ 288c2ecf20Sopenharmony_ci/* and an artificial extension */ 298c2ecf20Sopenharmony_ci#define DIAG_FTP_RET_EPERM 2 /* HMC FTP service privilege error */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* FTP service status codes (after INTR at guest real location 133) */ 328c2ecf20Sopenharmony_ci#define DIAG_FTP_STAT_OK 0U /* request completed successfully */ 338c2ecf20Sopenharmony_ci#define DIAG_FTP_STAT_PGCC 4U /* program check condition */ 348c2ecf20Sopenharmony_ci#define DIAG_FTP_STAT_PGIOE 8U /* paging I/O error */ 358c2ecf20Sopenharmony_ci#define DIAG_FTP_STAT_TIMEOUT 12U /* timeout */ 368c2ecf20Sopenharmony_ci#define DIAG_FTP_STAT_EBASE 16U /* base of error codes from SCLP */ 378c2ecf20Sopenharmony_ci#define DIAG_FTP_STAT_LDFAIL (DIAG_FTP_STAT_EBASE + 1U) /* failed */ 388c2ecf20Sopenharmony_ci#define DIAG_FTP_STAT_LDNPERM (DIAG_FTP_STAT_EBASE + 2U) /* not allowed */ 398c2ecf20Sopenharmony_ci#define DIAG_FTP_STAT_LDRUNS (DIAG_FTP_STAT_EBASE + 3U) /* runs */ 408c2ecf20Sopenharmony_ci#define DIAG_FTP_STAT_LDNRUNS (DIAG_FTP_STAT_EBASE + 4U) /* not runs */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/** 438c2ecf20Sopenharmony_ci * struct diag_ftp_ldfpl - load file FTP parameter list (LDFPL) 448c2ecf20Sopenharmony_ci * @bufaddr: real buffer address (at 4k boundary) 458c2ecf20Sopenharmony_ci * @buflen: length of buffer 468c2ecf20Sopenharmony_ci * @offset: dir/file offset 478c2ecf20Sopenharmony_ci * @intparm: interruption parameter (unused) 488c2ecf20Sopenharmony_ci * @transferred: bytes transferred 498c2ecf20Sopenharmony_ci * @fsize: file size, filled on GET 508c2ecf20Sopenharmony_ci * @failaddr: failing address 518c2ecf20Sopenharmony_ci * @spare: padding 528c2ecf20Sopenharmony_ci * @fident: file name - ASCII 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_cistruct diag_ftp_ldfpl { 558c2ecf20Sopenharmony_ci u64 bufaddr; 568c2ecf20Sopenharmony_ci u64 buflen; 578c2ecf20Sopenharmony_ci u64 offset; 588c2ecf20Sopenharmony_ci u64 intparm; 598c2ecf20Sopenharmony_ci u64 transferred; 608c2ecf20Sopenharmony_ci u64 fsize; 618c2ecf20Sopenharmony_ci u64 failaddr; 628c2ecf20Sopenharmony_ci u64 spare; 638c2ecf20Sopenharmony_ci u8 fident[HMCDRV_FTP_FIDENT_MAX]; 648c2ecf20Sopenharmony_ci} __packed; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic DECLARE_COMPLETION(diag_ftp_rx_complete); 678c2ecf20Sopenharmony_cistatic int diag_ftp_subcode; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/** 708c2ecf20Sopenharmony_ci * diag_ftp_handler() - FTP services IRQ handler 718c2ecf20Sopenharmony_ci * @extirq: external interrupt (sub-) code 728c2ecf20Sopenharmony_ci * @param32: 32-bit interruption parameter from &struct diag_ftp_ldfpl 738c2ecf20Sopenharmony_ci * @param64: unused (for 64-bit interrupt parameters) 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_cistatic void diag_ftp_handler(struct ext_code extirq, 768c2ecf20Sopenharmony_ci unsigned int param32, 778c2ecf20Sopenharmony_ci unsigned long param64) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci if ((extirq.subcode >> 8) != 8) 808c2ecf20Sopenharmony_ci return; /* not a FTP services sub-code */ 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci inc_irq_stat(IRQEXT_FTP); 838c2ecf20Sopenharmony_ci diag_ftp_subcode = extirq.subcode & 0xffU; 848c2ecf20Sopenharmony_ci complete(&diag_ftp_rx_complete); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/** 888c2ecf20Sopenharmony_ci * diag_ftp_2c4() - DIAGNOSE X'2C4' service call 898c2ecf20Sopenharmony_ci * @fpl: pointer to prepared LDFPL 908c2ecf20Sopenharmony_ci * @cmd: FTP command to be executed 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * Performs a DIAGNOSE X'2C4' call with (input/output) FTP parameter list 938c2ecf20Sopenharmony_ci * @fpl and FTP function code @cmd. In case of an error the function does 948c2ecf20Sopenharmony_ci * nothing and returns an (negative) error code. 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * Notes: 978c2ecf20Sopenharmony_ci * 1. This function only initiates a transfer, so the caller must wait 988c2ecf20Sopenharmony_ci * for completion (asynchronous execution). 998c2ecf20Sopenharmony_ci * 2. The FTP parameter list @fpl must be aligned to a double-word boundary. 1008c2ecf20Sopenharmony_ci * 3. fpl->bufaddr must be a real address, 4k aligned 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_cistatic int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl, 1038c2ecf20Sopenharmony_ci enum hmcdrv_ftp_cmdid cmd) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci int rc; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci diag_stat_inc(DIAG_STAT_X2C4); 1088c2ecf20Sopenharmony_ci asm volatile( 1098c2ecf20Sopenharmony_ci " diag %[addr],%[cmd],0x2c4\n" 1108c2ecf20Sopenharmony_ci "0: j 2f\n" 1118c2ecf20Sopenharmony_ci "1: la %[rc],%[err]\n" 1128c2ecf20Sopenharmony_ci "2:\n" 1138c2ecf20Sopenharmony_ci EX_TABLE(0b, 1b) 1148c2ecf20Sopenharmony_ci : [rc] "=d" (rc), "+m" (*fpl) 1158c2ecf20Sopenharmony_ci : [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)), 1168c2ecf20Sopenharmony_ci [err] "i" (DIAG_FTP_RET_EPERM) 1178c2ecf20Sopenharmony_ci : "cc"); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci switch (rc) { 1208c2ecf20Sopenharmony_ci case DIAG_FTP_RET_OK: 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci case DIAG_FTP_RET_EBUSY: 1238c2ecf20Sopenharmony_ci return -EBUSY; 1248c2ecf20Sopenharmony_ci case DIAG_FTP_RET_EPERM: 1258c2ecf20Sopenharmony_ci return -EPERM; 1268c2ecf20Sopenharmony_ci case DIAG_FTP_RET_EIO: 1278c2ecf20Sopenharmony_ci default: 1288c2ecf20Sopenharmony_ci return -EIO; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci/** 1338c2ecf20Sopenharmony_ci * diag_ftp_cmd() - executes a DIAG X'2C4' FTP command, targeting a HMC 1348c2ecf20Sopenharmony_ci * @ftp: pointer to FTP command specification 1358c2ecf20Sopenharmony_ci * @fsize: return of file size (or NULL if undesirable) 1368c2ecf20Sopenharmony_ci * 1378c2ecf20Sopenharmony_ci * Attention: Notice that this function is not reentrant - so the caller 1388c2ecf20Sopenharmony_ci * must ensure locking. 1398c2ecf20Sopenharmony_ci * 1408c2ecf20Sopenharmony_ci * Return: number of bytes read/written or a (negative) error code 1418c2ecf20Sopenharmony_ci */ 1428c2ecf20Sopenharmony_cissize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct diag_ftp_ldfpl *ldfpl; 1458c2ecf20Sopenharmony_ci ssize_t len; 1468c2ecf20Sopenharmony_ci#ifdef DEBUG 1478c2ecf20Sopenharmony_ci unsigned long start_jiffies; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci pr_debug("starting DIAG X'2C4' on '%s', requesting %zd bytes\n", 1508c2ecf20Sopenharmony_ci ftp->fname, ftp->len); 1518c2ecf20Sopenharmony_ci start_jiffies = jiffies; 1528c2ecf20Sopenharmony_ci#endif 1538c2ecf20Sopenharmony_ci init_completion(&diag_ftp_rx_complete); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci ldfpl = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 1568c2ecf20Sopenharmony_ci if (!ldfpl) { 1578c2ecf20Sopenharmony_ci len = -ENOMEM; 1588c2ecf20Sopenharmony_ci goto out; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident)); 1628c2ecf20Sopenharmony_ci if (len >= HMCDRV_FTP_FIDENT_MAX) { 1638c2ecf20Sopenharmony_ci len = -EINVAL; 1648c2ecf20Sopenharmony_ci goto out_free; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci ldfpl->transferred = 0; 1688c2ecf20Sopenharmony_ci ldfpl->fsize = 0; 1698c2ecf20Sopenharmony_ci ldfpl->offset = ftp->ofs; 1708c2ecf20Sopenharmony_ci ldfpl->buflen = ftp->len; 1718c2ecf20Sopenharmony_ci ldfpl->bufaddr = virt_to_phys(ftp->buf); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci len = diag_ftp_2c4(ldfpl, ftp->id); 1748c2ecf20Sopenharmony_ci if (len) 1758c2ecf20Sopenharmony_ci goto out_free; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* 1788c2ecf20Sopenharmony_ci * There is no way to cancel the running diag X'2C4', the code 1798c2ecf20Sopenharmony_ci * needs to wait unconditionally until the transfer is complete. 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ci wait_for_completion(&diag_ftp_rx_complete); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci#ifdef DEBUG 1848c2ecf20Sopenharmony_ci pr_debug("completed DIAG X'2C4' after %lu ms\n", 1858c2ecf20Sopenharmony_ci (jiffies - start_jiffies) * 1000 / HZ); 1868c2ecf20Sopenharmony_ci pr_debug("status of DIAG X'2C4' is %u, with %lld/%lld bytes\n", 1878c2ecf20Sopenharmony_ci diag_ftp_subcode, ldfpl->transferred, ldfpl->fsize); 1888c2ecf20Sopenharmony_ci#endif 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci switch (diag_ftp_subcode) { 1918c2ecf20Sopenharmony_ci case DIAG_FTP_STAT_OK: /* success */ 1928c2ecf20Sopenharmony_ci len = ldfpl->transferred; 1938c2ecf20Sopenharmony_ci if (fsize) 1948c2ecf20Sopenharmony_ci *fsize = ldfpl->fsize; 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci case DIAG_FTP_STAT_LDNPERM: 1978c2ecf20Sopenharmony_ci len = -EPERM; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case DIAG_FTP_STAT_LDRUNS: 2008c2ecf20Sopenharmony_ci len = -EBUSY; 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci case DIAG_FTP_STAT_LDFAIL: 2038c2ecf20Sopenharmony_ci len = -ENOENT; /* no such file or media */ 2048c2ecf20Sopenharmony_ci break; 2058c2ecf20Sopenharmony_ci default: 2068c2ecf20Sopenharmony_ci len = -EIO; 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ciout_free: 2118c2ecf20Sopenharmony_ci free_page((unsigned long) ldfpl); 2128c2ecf20Sopenharmony_ciout: 2138c2ecf20Sopenharmony_ci return len; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/** 2178c2ecf20Sopenharmony_ci * diag_ftp_startup() - startup of FTP services, when running on z/VM 2188c2ecf20Sopenharmony_ci * 2198c2ecf20Sopenharmony_ci * Return: 0 on success, else an (negative) error code 2208c2ecf20Sopenharmony_ci */ 2218c2ecf20Sopenharmony_ciint diag_ftp_startup(void) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci int rc; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci rc = register_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler); 2268c2ecf20Sopenharmony_ci if (rc) 2278c2ecf20Sopenharmony_ci return rc; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci irq_subclass_register(IRQ_SUBCLASS_SERVICE_SIGNAL); 2308c2ecf20Sopenharmony_ci return 0; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/** 2348c2ecf20Sopenharmony_ci * diag_ftp_shutdown() - shutdown of FTP services, when running on z/VM 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_civoid diag_ftp_shutdown(void) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL); 2398c2ecf20Sopenharmony_ci unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler); 2408c2ecf20Sopenharmony_ci} 241