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