1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 28f933b10SRalf Hoppe /* 38f933b10SRalf Hoppe * HMC Drive FTP Services 48f933b10SRalf Hoppe * 58f933b10SRalf Hoppe * Copyright IBM Corp. 2013 68f933b10SRalf Hoppe * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) 78f933b10SRalf Hoppe */ 88f933b10SRalf Hoppe 98f933b10SRalf Hoppe #define KMSG_COMPONENT "hmcdrv" 108f933b10SRalf Hoppe #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 118f933b10SRalf Hoppe 128f933b10SRalf Hoppe #include <linux/kernel.h> 138f933b10SRalf Hoppe #include <linux/slab.h> 148f933b10SRalf Hoppe #include <linux/uaccess.h> 158f933b10SRalf Hoppe #include <linux/export.h> 168f933b10SRalf Hoppe 178f933b10SRalf Hoppe #include <linux/ctype.h> 188f933b10SRalf Hoppe #include <linux/crc16.h> 198f933b10SRalf Hoppe 208f933b10SRalf Hoppe #include "hmcdrv_ftp.h" 218f933b10SRalf Hoppe #include "hmcdrv_cache.h" 228f933b10SRalf Hoppe #include "sclp_ftp.h" 238f933b10SRalf Hoppe #include "diag_ftp.h" 248f933b10SRalf Hoppe 258f933b10SRalf Hoppe /** 268f933b10SRalf Hoppe * struct hmcdrv_ftp_ops - HMC drive FTP operations 278f933b10SRalf Hoppe * @startup: startup function 288f933b10SRalf Hoppe * @shutdown: shutdown function 298f933b10SRalf Hoppe * @cmd: FTP transfer function 308f933b10SRalf Hoppe */ 318f933b10SRalf Hoppe struct hmcdrv_ftp_ops { 328f933b10SRalf Hoppe int (*startup)(void); 338f933b10SRalf Hoppe void (*shutdown)(void); 348f933b10SRalf Hoppe ssize_t (*transfer)(const struct hmcdrv_ftp_cmdspec *ftp, 358f933b10SRalf Hoppe size_t *fsize); 368f933b10SRalf Hoppe }; 378f933b10SRalf Hoppe 388f933b10SRalf Hoppe static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len); 398f933b10SRalf Hoppe static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp); 408f933b10SRalf Hoppe 41c967e1dfSAya Mahfouz static const struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs; /* current operations */ 428f933b10SRalf Hoppe static DEFINE_MUTEX(hmcdrv_ftp_mutex); /* mutex for hmcdrv_ftp_funcs */ 438f933b10SRalf Hoppe static unsigned hmcdrv_ftp_refcnt; /* start/shutdown reference counter */ 448f933b10SRalf Hoppe 458f933b10SRalf Hoppe /** 468f933b10SRalf Hoppe * hmcdrv_ftp_cmd_getid() - determine FTP command ID from a command string 478f933b10SRalf Hoppe * @cmd: FTP command string (NOT zero-terminated) 488f933b10SRalf Hoppe * @len: length of FTP command string in @cmd 498f933b10SRalf Hoppe */ 508f933b10SRalf Hoppe static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len) 518f933b10SRalf Hoppe { 528f933b10SRalf Hoppe /* HMC FTP command descriptor */ 538f933b10SRalf Hoppe struct hmcdrv_ftp_cmd_desc { 548f933b10SRalf Hoppe const char *str; /* command string */ 558f933b10SRalf Hoppe enum hmcdrv_ftp_cmdid cmd; /* associated command as enum */ 568f933b10SRalf Hoppe }; 578f933b10SRalf Hoppe 588f933b10SRalf Hoppe /* Description of all HMC drive FTP commands 598f933b10SRalf Hoppe * 608f933b10SRalf Hoppe * Notes: 618f933b10SRalf Hoppe * 1. Array size should be a prime number. 628f933b10SRalf Hoppe * 2. Do not change the order of commands in table (because the 638f933b10SRalf Hoppe * index is determined by CRC % ARRAY_SIZE). 648f933b10SRalf Hoppe * 3. Original command 'nlist' was renamed, else the CRC would 658f933b10SRalf Hoppe * collide with 'append' (see point 2). 668f933b10SRalf Hoppe */ 678f933b10SRalf Hoppe static const struct hmcdrv_ftp_cmd_desc ftpcmds[7] = { 688f933b10SRalf Hoppe 698f933b10SRalf Hoppe {.str = "get", /* [0] get (CRC = 0x68eb) */ 708f933b10SRalf Hoppe .cmd = HMCDRV_FTP_GET}, 718f933b10SRalf Hoppe {.str = "dir", /* [1] dir (CRC = 0x6a9e) */ 728f933b10SRalf Hoppe .cmd = HMCDRV_FTP_DIR}, 738f933b10SRalf Hoppe {.str = "delete", /* [2] delete (CRC = 0x53ae) */ 748f933b10SRalf Hoppe .cmd = HMCDRV_FTP_DELETE}, 758f933b10SRalf Hoppe {.str = "nls", /* [3] nls (CRC = 0xf87c) */ 768f933b10SRalf Hoppe .cmd = HMCDRV_FTP_NLIST}, 778f933b10SRalf Hoppe {.str = "put", /* [4] put (CRC = 0xac56) */ 788f933b10SRalf Hoppe .cmd = HMCDRV_FTP_PUT}, 798f933b10SRalf Hoppe {.str = "append", /* [5] append (CRC = 0xf56e) */ 808f933b10SRalf Hoppe .cmd = HMCDRV_FTP_APPEND}, 818f933b10SRalf Hoppe {.str = NULL} /* [6] unused */ 828f933b10SRalf Hoppe }; 838f933b10SRalf Hoppe 848f933b10SRalf Hoppe const struct hmcdrv_ftp_cmd_desc *pdesc; 858f933b10SRalf Hoppe 868f933b10SRalf Hoppe u16 crc = 0xffffU; 878f933b10SRalf Hoppe 888f933b10SRalf Hoppe if (len == 0) 898f933b10SRalf Hoppe return HMCDRV_FTP_NOOP; /* error indiactor */ 908f933b10SRalf Hoppe 918f933b10SRalf Hoppe crc = crc16(crc, cmd, len); 928f933b10SRalf Hoppe pdesc = ftpcmds + (crc % ARRAY_SIZE(ftpcmds)); 938f933b10SRalf Hoppe pr_debug("FTP command '%s' has CRC 0x%04x, at table pos. %lu\n", 948f933b10SRalf Hoppe cmd, crc, (crc % ARRAY_SIZE(ftpcmds))); 958f933b10SRalf Hoppe 968f933b10SRalf Hoppe if (!pdesc->str || strncmp(pdesc->str, cmd, len)) 978f933b10SRalf Hoppe return HMCDRV_FTP_NOOP; 988f933b10SRalf Hoppe 998f933b10SRalf Hoppe pr_debug("FTP command '%s' found, with ID %d\n", 1008f933b10SRalf Hoppe pdesc->str, pdesc->cmd); 1018f933b10SRalf Hoppe 1028f933b10SRalf Hoppe return pdesc->cmd; 1038f933b10SRalf Hoppe } 1048f933b10SRalf Hoppe 1058f933b10SRalf Hoppe /** 1068f933b10SRalf Hoppe * hmcdrv_ftp_parse() - HMC drive FTP command parser 1078f933b10SRalf Hoppe * @cmd: FTP command string "<cmd> <filename>" 1088f933b10SRalf Hoppe * @ftp: Pointer to FTP command specification buffer (output) 1098f933b10SRalf Hoppe * 1108f933b10SRalf Hoppe * Return: 0 on success, else a (negative) error code 1118f933b10SRalf Hoppe */ 1128f933b10SRalf Hoppe static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp) 1138f933b10SRalf Hoppe { 1148f933b10SRalf Hoppe char *start; 1158f933b10SRalf Hoppe int argc = 0; 1168f933b10SRalf Hoppe 1178f933b10SRalf Hoppe ftp->id = HMCDRV_FTP_NOOP; 1188f933b10SRalf Hoppe ftp->fname = NULL; 1198f933b10SRalf Hoppe 1208f933b10SRalf Hoppe while (*cmd != '\0') { 1218f933b10SRalf Hoppe 1228f933b10SRalf Hoppe while (isspace(*cmd)) 1238f933b10SRalf Hoppe ++cmd; 1248f933b10SRalf Hoppe 1258f933b10SRalf Hoppe if (*cmd == '\0') 1268f933b10SRalf Hoppe break; 1278f933b10SRalf Hoppe 1288f933b10SRalf Hoppe start = cmd; 1298f933b10SRalf Hoppe 1308f933b10SRalf Hoppe switch (argc) { 1318f933b10SRalf Hoppe case 0: /* 1st argument (FTP command) */ 1328f933b10SRalf Hoppe while ((*cmd != '\0') && !isspace(*cmd)) 1338f933b10SRalf Hoppe ++cmd; 1348f933b10SRalf Hoppe ftp->id = hmcdrv_ftp_cmd_getid(start, cmd - start); 1358f933b10SRalf Hoppe break; 1368f933b10SRalf Hoppe case 1: /* 2nd / last argument (rest of line) */ 1378f933b10SRalf Hoppe while ((*cmd != '\0') && !iscntrl(*cmd)) 1388f933b10SRalf Hoppe ++cmd; 1398f933b10SRalf Hoppe ftp->fname = start; 140*2c7749b9SJoe Perches fallthrough; 1418f933b10SRalf Hoppe default: 1428f933b10SRalf Hoppe *cmd = '\0'; 1438f933b10SRalf Hoppe break; 1448f933b10SRalf Hoppe } /* switch */ 1458f933b10SRalf Hoppe 1468f933b10SRalf Hoppe ++argc; 1478f933b10SRalf Hoppe } /* while */ 1488f933b10SRalf Hoppe 1498f933b10SRalf Hoppe if (!ftp->fname || (ftp->id == HMCDRV_FTP_NOOP)) 1508f933b10SRalf Hoppe return -EINVAL; 1518f933b10SRalf Hoppe 1528f933b10SRalf Hoppe return 0; 1538f933b10SRalf Hoppe } 1548f933b10SRalf Hoppe 1558f933b10SRalf Hoppe /** 1568f933b10SRalf Hoppe * hmcdrv_ftp_do() - perform a HMC drive FTP, with data from kernel-space 1578f933b10SRalf Hoppe * @ftp: pointer to FTP command specification 1588f933b10SRalf Hoppe * 1598f933b10SRalf Hoppe * Return: number of bytes read/written or a negative error code 1608f933b10SRalf Hoppe */ 1618f933b10SRalf Hoppe ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp) 1628f933b10SRalf Hoppe { 1638f933b10SRalf Hoppe ssize_t len; 1648f933b10SRalf Hoppe 1658f933b10SRalf Hoppe mutex_lock(&hmcdrv_ftp_mutex); 1668f933b10SRalf Hoppe 1678f933b10SRalf Hoppe if (hmcdrv_ftp_funcs && hmcdrv_ftp_refcnt) { 1688f933b10SRalf Hoppe pr_debug("starting transfer, cmd %d for '%s' at %lld with %zd bytes\n", 1698f933b10SRalf Hoppe ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); 1708f933b10SRalf Hoppe len = hmcdrv_cache_cmd(ftp, hmcdrv_ftp_funcs->transfer); 1718f933b10SRalf Hoppe } else { 1728f933b10SRalf Hoppe len = -ENXIO; 1738f933b10SRalf Hoppe } 1748f933b10SRalf Hoppe 1758f933b10SRalf Hoppe mutex_unlock(&hmcdrv_ftp_mutex); 1768f933b10SRalf Hoppe return len; 1778f933b10SRalf Hoppe } 1788f933b10SRalf Hoppe EXPORT_SYMBOL(hmcdrv_ftp_do); 1798f933b10SRalf Hoppe 1808f933b10SRalf Hoppe /** 1818f933b10SRalf Hoppe * hmcdrv_ftp_probe() - probe for the HMC drive FTP service 1828f933b10SRalf Hoppe * 1838f933b10SRalf Hoppe * Return: 0 if service is available, else an (negative) error code 1848f933b10SRalf Hoppe */ 1858f933b10SRalf Hoppe int hmcdrv_ftp_probe(void) 1868f933b10SRalf Hoppe { 1878f933b10SRalf Hoppe int rc; 1888f933b10SRalf Hoppe 1898f933b10SRalf Hoppe struct hmcdrv_ftp_cmdspec ftp = { 1908f933b10SRalf Hoppe .id = HMCDRV_FTP_NOOP, 1918f933b10SRalf Hoppe .ofs = 0, 1928f933b10SRalf Hoppe .fname = "", 1938f933b10SRalf Hoppe .len = PAGE_SIZE 1948f933b10SRalf Hoppe }; 1958f933b10SRalf Hoppe 1968f933b10SRalf Hoppe ftp.buf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); 1978f933b10SRalf Hoppe 1988f933b10SRalf Hoppe if (!ftp.buf) 1998f933b10SRalf Hoppe return -ENOMEM; 2008f933b10SRalf Hoppe 2018f933b10SRalf Hoppe rc = hmcdrv_ftp_startup(); 2028f933b10SRalf Hoppe 2038f933b10SRalf Hoppe if (rc) 2042ec50493SChristophe Jaillet goto out; 2058f933b10SRalf Hoppe 2068f933b10SRalf Hoppe rc = hmcdrv_ftp_do(&ftp); 2078f933b10SRalf Hoppe hmcdrv_ftp_shutdown(); 2088f933b10SRalf Hoppe 2098f933b10SRalf Hoppe switch (rc) { 2108f933b10SRalf Hoppe case -ENOENT: /* no such file/media or currently busy, */ 2118f933b10SRalf Hoppe case -EBUSY: /* but service seems to be available */ 2128f933b10SRalf Hoppe rc = 0; 2138f933b10SRalf Hoppe break; 2148f933b10SRalf Hoppe default: /* leave 'rc' as it is for [0, -EPERM, -E...] */ 2158f933b10SRalf Hoppe if (rc > 0) 2168f933b10SRalf Hoppe rc = 0; /* clear length (success) */ 2178f933b10SRalf Hoppe break; 2188f933b10SRalf Hoppe } /* switch */ 2192ec50493SChristophe Jaillet out: 2202ec50493SChristophe Jaillet free_page((unsigned long) ftp.buf); 2218f933b10SRalf Hoppe return rc; 2228f933b10SRalf Hoppe } 2238f933b10SRalf Hoppe EXPORT_SYMBOL(hmcdrv_ftp_probe); 2248f933b10SRalf Hoppe 2258f933b10SRalf Hoppe /** 2268f933b10SRalf Hoppe * hmcdrv_ftp_cmd() - Perform a HMC drive FTP, with data from user-space 2278f933b10SRalf Hoppe * 2288f933b10SRalf Hoppe * @cmd: FTP command string "<cmd> <filename>" 2298f933b10SRalf Hoppe * @offset: file position to read/write 2308f933b10SRalf Hoppe * @buf: user-space buffer for read/written directory/file 2318f933b10SRalf Hoppe * @len: size of @buf (read/dir) or number of bytes to write 2328f933b10SRalf Hoppe * 2338f933b10SRalf Hoppe * This function must not be called before hmcdrv_ftp_startup() was called. 2348f933b10SRalf Hoppe * 2358f933b10SRalf Hoppe * Return: number of bytes read/written or a negative error code 2368f933b10SRalf Hoppe */ 2378f933b10SRalf Hoppe ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset, 2388f933b10SRalf Hoppe char __user *buf, size_t len) 2398f933b10SRalf Hoppe { 2408f933b10SRalf Hoppe int order; 2418f933b10SRalf Hoppe 2428f933b10SRalf Hoppe struct hmcdrv_ftp_cmdspec ftp = {.len = len, .ofs = offset}; 2438f933b10SRalf Hoppe ssize_t retlen = hmcdrv_ftp_parse(cmd, &ftp); 2448f933b10SRalf Hoppe 2458f933b10SRalf Hoppe if (retlen) 2468f933b10SRalf Hoppe return retlen; 2478f933b10SRalf Hoppe 2488f933b10SRalf Hoppe order = get_order(ftp.len); 2498f933b10SRalf Hoppe ftp.buf = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, order); 2508f933b10SRalf Hoppe 2518f933b10SRalf Hoppe if (!ftp.buf) 2528f933b10SRalf Hoppe return -ENOMEM; 2538f933b10SRalf Hoppe 2548f933b10SRalf Hoppe switch (ftp.id) { 2558f933b10SRalf Hoppe case HMCDRV_FTP_DIR: 2568f933b10SRalf Hoppe case HMCDRV_FTP_NLIST: 2578f933b10SRalf Hoppe case HMCDRV_FTP_GET: 2588f933b10SRalf Hoppe retlen = hmcdrv_ftp_do(&ftp); 2598f933b10SRalf Hoppe 2608f933b10SRalf Hoppe if ((retlen >= 0) && 2618f933b10SRalf Hoppe copy_to_user(buf, ftp.buf, retlen)) 2628f933b10SRalf Hoppe retlen = -EFAULT; 2638f933b10SRalf Hoppe break; 2648f933b10SRalf Hoppe 2658f933b10SRalf Hoppe case HMCDRV_FTP_PUT: 2668f933b10SRalf Hoppe case HMCDRV_FTP_APPEND: 2678f933b10SRalf Hoppe if (!copy_from_user(ftp.buf, buf, ftp.len)) 2688f933b10SRalf Hoppe retlen = hmcdrv_ftp_do(&ftp); 2698f933b10SRalf Hoppe else 2708f933b10SRalf Hoppe retlen = -EFAULT; 2718f933b10SRalf Hoppe break; 2728f933b10SRalf Hoppe 2738f933b10SRalf Hoppe case HMCDRV_FTP_DELETE: 2748f933b10SRalf Hoppe retlen = hmcdrv_ftp_do(&ftp); 2758f933b10SRalf Hoppe break; 2768f933b10SRalf Hoppe 2778f933b10SRalf Hoppe default: 2788f933b10SRalf Hoppe retlen = -EOPNOTSUPP; 2798f933b10SRalf Hoppe break; 2808f933b10SRalf Hoppe } 2818f933b10SRalf Hoppe 2828f933b10SRalf Hoppe free_pages((unsigned long) ftp.buf, order); 2838f933b10SRalf Hoppe return retlen; 2848f933b10SRalf Hoppe } 2858f933b10SRalf Hoppe 2868f933b10SRalf Hoppe /** 2878f933b10SRalf Hoppe * hmcdrv_ftp_startup() - startup of HMC drive FTP functionality for a 2888f933b10SRalf Hoppe * dedicated (owner) instance 2898f933b10SRalf Hoppe * 2908f933b10SRalf Hoppe * Return: 0 on success, else an (negative) error code 2918f933b10SRalf Hoppe */ 2928f933b10SRalf Hoppe int hmcdrv_ftp_startup(void) 2938f933b10SRalf Hoppe { 294c967e1dfSAya Mahfouz static const struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = { 2958f933b10SRalf Hoppe .startup = diag_ftp_startup, 2968f933b10SRalf Hoppe .shutdown = diag_ftp_shutdown, 2978f933b10SRalf Hoppe .transfer = diag_ftp_cmd 2988f933b10SRalf Hoppe }; 2998f933b10SRalf Hoppe 300c967e1dfSAya Mahfouz static const struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = { 3018f933b10SRalf Hoppe .startup = sclp_ftp_startup, 3028f933b10SRalf Hoppe .shutdown = sclp_ftp_shutdown, 3038f933b10SRalf Hoppe .transfer = sclp_ftp_cmd 3048f933b10SRalf Hoppe }; 3058f933b10SRalf Hoppe 3068f933b10SRalf Hoppe int rc = 0; 3078f933b10SRalf Hoppe 3088f933b10SRalf Hoppe mutex_lock(&hmcdrv_ftp_mutex); /* block transfers while start-up */ 3098f933b10SRalf Hoppe 3108f933b10SRalf Hoppe if (hmcdrv_ftp_refcnt == 0) { 3118f933b10SRalf Hoppe if (MACHINE_IS_VM) 3128f933b10SRalf Hoppe hmcdrv_ftp_funcs = &hmcdrv_ftp_zvm; 3138f933b10SRalf Hoppe else if (MACHINE_IS_LPAR || MACHINE_IS_KVM) 3148f933b10SRalf Hoppe hmcdrv_ftp_funcs = &hmcdrv_ftp_lpar; 3158f933b10SRalf Hoppe else 3168f933b10SRalf Hoppe rc = -EOPNOTSUPP; 3178f933b10SRalf Hoppe 3188f933b10SRalf Hoppe if (hmcdrv_ftp_funcs) 3198f933b10SRalf Hoppe rc = hmcdrv_ftp_funcs->startup(); 3208f933b10SRalf Hoppe } 3218f933b10SRalf Hoppe 3228f933b10SRalf Hoppe if (!rc) 3238f933b10SRalf Hoppe ++hmcdrv_ftp_refcnt; 3248f933b10SRalf Hoppe 3258f933b10SRalf Hoppe mutex_unlock(&hmcdrv_ftp_mutex); 3268f933b10SRalf Hoppe return rc; 3278f933b10SRalf Hoppe } 3288f933b10SRalf Hoppe EXPORT_SYMBOL(hmcdrv_ftp_startup); 3298f933b10SRalf Hoppe 3308f933b10SRalf Hoppe /** 3318f933b10SRalf Hoppe * hmcdrv_ftp_shutdown() - shutdown of HMC drive FTP functionality for a 3328f933b10SRalf Hoppe * dedicated (owner) instance 3338f933b10SRalf Hoppe */ 3348f933b10SRalf Hoppe void hmcdrv_ftp_shutdown(void) 3358f933b10SRalf Hoppe { 3368f933b10SRalf Hoppe mutex_lock(&hmcdrv_ftp_mutex); 3378f933b10SRalf Hoppe --hmcdrv_ftp_refcnt; 3388f933b10SRalf Hoppe 3398f933b10SRalf Hoppe if ((hmcdrv_ftp_refcnt == 0) && hmcdrv_ftp_funcs) 3408f933b10SRalf Hoppe hmcdrv_ftp_funcs->shutdown(); 3418f933b10SRalf Hoppe 3428f933b10SRalf Hoppe mutex_unlock(&hmcdrv_ftp_mutex); 3438f933b10SRalf Hoppe } 3448f933b10SRalf Hoppe EXPORT_SYMBOL(hmcdrv_ftp_shutdown); 345