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