345 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * HMC Drive FTP Services4 *5 * Copyright IBM Corp. 20136 * Author(s): Ralf Hoppe (rhoppe@de.ibm.com)7 */8 9#define KMSG_COMPONENT "hmcdrv"10#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt11 12#include <linux/kernel.h>13#include <linux/slab.h>14#include <linux/uaccess.h>15#include <linux/export.h>16 17#include <linux/ctype.h>18#include <linux/crc16.h>19 20#include "hmcdrv_ftp.h"21#include "hmcdrv_cache.h"22#include "sclp_ftp.h"23#include "diag_ftp.h"24 25/**26 * struct hmcdrv_ftp_ops - HMC drive FTP operations27 * @startup: startup function28 * @shutdown: shutdown function29 * @transfer: FTP transfer function30 */31struct hmcdrv_ftp_ops {32 int (*startup)(void);33 void (*shutdown)(void);34 ssize_t (*transfer)(const struct hmcdrv_ftp_cmdspec *ftp,35 size_t *fsize);36};37 38static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len);39static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp);40 41static const struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs; /* current operations */42static DEFINE_MUTEX(hmcdrv_ftp_mutex); /* mutex for hmcdrv_ftp_funcs */43static unsigned hmcdrv_ftp_refcnt; /* start/shutdown reference counter */44 45/**46 * hmcdrv_ftp_cmd_getid() - determine FTP command ID from a command string47 * @cmd: FTP command string (NOT zero-terminated)48 * @len: length of FTP command string in @cmd49 */50static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len)51{52 /* HMC FTP command descriptor */53 struct hmcdrv_ftp_cmd_desc {54 const char *str; /* command string */55 enum hmcdrv_ftp_cmdid cmd; /* associated command as enum */56 };57 58 /* Description of all HMC drive FTP commands59 *60 * Notes:61 * 1. Array size should be a prime number.62 * 2. Do not change the order of commands in table (because the63 * index is determined by CRC % ARRAY_SIZE).64 * 3. Original command 'nlist' was renamed, else the CRC would65 * collide with 'append' (see point 2).66 */67 static const struct hmcdrv_ftp_cmd_desc ftpcmds[7] = {68 69 {.str = "get", /* [0] get (CRC = 0x68eb) */70 .cmd = HMCDRV_FTP_GET},71 {.str = "dir", /* [1] dir (CRC = 0x6a9e) */72 .cmd = HMCDRV_FTP_DIR},73 {.str = "delete", /* [2] delete (CRC = 0x53ae) */74 .cmd = HMCDRV_FTP_DELETE},75 {.str = "nls", /* [3] nls (CRC = 0xf87c) */76 .cmd = HMCDRV_FTP_NLIST},77 {.str = "put", /* [4] put (CRC = 0xac56) */78 .cmd = HMCDRV_FTP_PUT},79 {.str = "append", /* [5] append (CRC = 0xf56e) */80 .cmd = HMCDRV_FTP_APPEND},81 {.str = NULL} /* [6] unused */82 };83 84 const struct hmcdrv_ftp_cmd_desc *pdesc;85 86 u16 crc = 0xffffU;87 88 if (len == 0)89 return HMCDRV_FTP_NOOP; /* error indiactor */90 91 crc = crc16(crc, cmd, len);92 pdesc = ftpcmds + (crc % ARRAY_SIZE(ftpcmds));93 pr_debug("FTP command '%s' has CRC 0x%04x, at table pos. %lu\n",94 cmd, crc, (crc % ARRAY_SIZE(ftpcmds)));95 96 if (!pdesc->str || strncmp(pdesc->str, cmd, len))97 return HMCDRV_FTP_NOOP;98 99 pr_debug("FTP command '%s' found, with ID %d\n",100 pdesc->str, pdesc->cmd);101 102 return pdesc->cmd;103}104 105/**106 * hmcdrv_ftp_parse() - HMC drive FTP command parser107 * @cmd: FTP command string "<cmd> <filename>"108 * @ftp: Pointer to FTP command specification buffer (output)109 *110 * Return: 0 on success, else a (negative) error code111 */112static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp)113{114 char *start;115 int argc = 0;116 117 ftp->id = HMCDRV_FTP_NOOP;118 ftp->fname = NULL;119 120 while (*cmd != '\0') {121 122 while (isspace(*cmd))123 ++cmd;124 125 if (*cmd == '\0')126 break;127 128 start = cmd;129 130 switch (argc) {131 case 0: /* 1st argument (FTP command) */132 while ((*cmd != '\0') && !isspace(*cmd))133 ++cmd;134 ftp->id = hmcdrv_ftp_cmd_getid(start, cmd - start);135 break;136 case 1: /* 2nd / last argument (rest of line) */137 while ((*cmd != '\0') && !iscntrl(*cmd))138 ++cmd;139 ftp->fname = start;140 fallthrough;141 default:142 *cmd = '\0';143 break;144 } /* switch */145 146 ++argc;147 } /* while */148 149 if (!ftp->fname || (ftp->id == HMCDRV_FTP_NOOP))150 return -EINVAL;151 152 return 0;153}154 155/**156 * hmcdrv_ftp_do() - perform a HMC drive FTP, with data from kernel-space157 * @ftp: pointer to FTP command specification158 *159 * Return: number of bytes read/written or a negative error code160 */161ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp)162{163 ssize_t len;164 165 mutex_lock(&hmcdrv_ftp_mutex);166 167 if (hmcdrv_ftp_funcs && hmcdrv_ftp_refcnt) {168 pr_debug("starting transfer, cmd %d for '%s' at %lld with %zd bytes\n",169 ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len);170 len = hmcdrv_cache_cmd(ftp, hmcdrv_ftp_funcs->transfer);171 } else {172 len = -ENXIO;173 }174 175 mutex_unlock(&hmcdrv_ftp_mutex);176 return len;177}178EXPORT_SYMBOL(hmcdrv_ftp_do);179 180/**181 * hmcdrv_ftp_probe() - probe for the HMC drive FTP service182 *183 * Return: 0 if service is available, else an (negative) error code184 */185int hmcdrv_ftp_probe(void)186{187 int rc;188 189 struct hmcdrv_ftp_cmdspec ftp = {190 .id = HMCDRV_FTP_NOOP,191 .ofs = 0,192 .fname = "",193 .len = PAGE_SIZE194 };195 196 ftp.buf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);197 198 if (!ftp.buf)199 return -ENOMEM;200 201 rc = hmcdrv_ftp_startup();202 203 if (rc)204 goto out;205 206 rc = hmcdrv_ftp_do(&ftp);207 hmcdrv_ftp_shutdown();208 209 switch (rc) {210 case -ENOENT: /* no such file/media or currently busy, */211 case -EBUSY: /* but service seems to be available */212 rc = 0;213 break;214 default: /* leave 'rc' as it is for [0, -EPERM, -E...] */215 if (rc > 0)216 rc = 0; /* clear length (success) */217 break;218 } /* switch */219out:220 free_page((unsigned long) ftp.buf);221 return rc;222}223EXPORT_SYMBOL(hmcdrv_ftp_probe);224 225/**226 * hmcdrv_ftp_cmd() - Perform a HMC drive FTP, with data from user-space227 *228 * @cmd: FTP command string "<cmd> <filename>"229 * @offset: file position to read/write230 * @buf: user-space buffer for read/written directory/file231 * @len: size of @buf (read/dir) or number of bytes to write232 *233 * This function must not be called before hmcdrv_ftp_startup() was called.234 *235 * Return: number of bytes read/written or a negative error code236 */237ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset,238 char __user *buf, size_t len)239{240 int order;241 242 struct hmcdrv_ftp_cmdspec ftp = {.len = len, .ofs = offset};243 ssize_t retlen = hmcdrv_ftp_parse(cmd, &ftp);244 245 if (retlen)246 return retlen;247 248 order = get_order(ftp.len);249 ftp.buf = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, order);250 251 if (!ftp.buf)252 return -ENOMEM;253 254 switch (ftp.id) {255 case HMCDRV_FTP_DIR:256 case HMCDRV_FTP_NLIST:257 case HMCDRV_FTP_GET:258 retlen = hmcdrv_ftp_do(&ftp);259 260 if ((retlen >= 0) &&261 copy_to_user(buf, ftp.buf, retlen))262 retlen = -EFAULT;263 break;264 265 case HMCDRV_FTP_PUT:266 case HMCDRV_FTP_APPEND:267 if (!copy_from_user(ftp.buf, buf, ftp.len))268 retlen = hmcdrv_ftp_do(&ftp);269 else270 retlen = -EFAULT;271 break;272 273 case HMCDRV_FTP_DELETE:274 retlen = hmcdrv_ftp_do(&ftp);275 break;276 277 default:278 retlen = -EOPNOTSUPP;279 break;280 }281 282 free_pages((unsigned long) ftp.buf, order);283 return retlen;284}285 286/**287 * hmcdrv_ftp_startup() - startup of HMC drive FTP functionality for a288 * dedicated (owner) instance289 *290 * Return: 0 on success, else an (negative) error code291 */292int hmcdrv_ftp_startup(void)293{294 static const struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = {295 .startup = diag_ftp_startup,296 .shutdown = diag_ftp_shutdown,297 .transfer = diag_ftp_cmd298 };299 300 static const struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = {301 .startup = sclp_ftp_startup,302 .shutdown = sclp_ftp_shutdown,303 .transfer = sclp_ftp_cmd304 };305 306 int rc = 0;307 308 mutex_lock(&hmcdrv_ftp_mutex); /* block transfers while start-up */309 310 if (hmcdrv_ftp_refcnt == 0) {311 if (MACHINE_IS_VM)312 hmcdrv_ftp_funcs = &hmcdrv_ftp_zvm;313 else if (MACHINE_IS_LPAR || MACHINE_IS_KVM)314 hmcdrv_ftp_funcs = &hmcdrv_ftp_lpar;315 else316 rc = -EOPNOTSUPP;317 318 if (hmcdrv_ftp_funcs)319 rc = hmcdrv_ftp_funcs->startup();320 }321 322 if (!rc)323 ++hmcdrv_ftp_refcnt;324 325 mutex_unlock(&hmcdrv_ftp_mutex);326 return rc;327}328EXPORT_SYMBOL(hmcdrv_ftp_startup);329 330/**331 * hmcdrv_ftp_shutdown() - shutdown of HMC drive FTP functionality for a332 * dedicated (owner) instance333 */334void hmcdrv_ftp_shutdown(void)335{336 mutex_lock(&hmcdrv_ftp_mutex);337 --hmcdrv_ftp_refcnt;338 339 if ((hmcdrv_ftp_refcnt == 0) && hmcdrv_ftp_funcs)340 hmcdrv_ftp_funcs->shutdown();341 342 mutex_unlock(&hmcdrv_ftp_mutex);343}344EXPORT_SYMBOL(hmcdrv_ftp_shutdown);345