0001
0002
0003
0004
0005
0006
0007
0008
0009 #define KMSG_COMPONENT "hmcdrv"
0010 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
0011
0012 #include <linux/kernel.h>
0013 #include <linux/slab.h>
0014 #include <linux/uaccess.h>
0015 #include <linux/export.h>
0016
0017 #include <linux/ctype.h>
0018 #include <linux/crc16.h>
0019
0020 #include "hmcdrv_ftp.h"
0021 #include "hmcdrv_cache.h"
0022 #include "sclp_ftp.h"
0023 #include "diag_ftp.h"
0024
0025
0026
0027
0028
0029
0030
0031 struct hmcdrv_ftp_ops {
0032 int (*startup)(void);
0033 void (*shutdown)(void);
0034 ssize_t (*transfer)(const struct hmcdrv_ftp_cmdspec *ftp,
0035 size_t *fsize);
0036 };
0037
0038 static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len);
0039 static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp);
0040
0041 static const struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs;
0042 static DEFINE_MUTEX(hmcdrv_ftp_mutex);
0043 static unsigned hmcdrv_ftp_refcnt;
0044
0045
0046
0047
0048
0049
0050 static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len)
0051 {
0052
0053 struct hmcdrv_ftp_cmd_desc {
0054 const char *str;
0055 enum hmcdrv_ftp_cmdid cmd;
0056 };
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067 static const struct hmcdrv_ftp_cmd_desc ftpcmds[7] = {
0068
0069 {.str = "get",
0070 .cmd = HMCDRV_FTP_GET},
0071 {.str = "dir",
0072 .cmd = HMCDRV_FTP_DIR},
0073 {.str = "delete",
0074 .cmd = HMCDRV_FTP_DELETE},
0075 {.str = "nls",
0076 .cmd = HMCDRV_FTP_NLIST},
0077 {.str = "put",
0078 .cmd = HMCDRV_FTP_PUT},
0079 {.str = "append",
0080 .cmd = HMCDRV_FTP_APPEND},
0081 {.str = NULL}
0082 };
0083
0084 const struct hmcdrv_ftp_cmd_desc *pdesc;
0085
0086 u16 crc = 0xffffU;
0087
0088 if (len == 0)
0089 return HMCDRV_FTP_NOOP;
0090
0091 crc = crc16(crc, cmd, len);
0092 pdesc = ftpcmds + (crc % ARRAY_SIZE(ftpcmds));
0093 pr_debug("FTP command '%s' has CRC 0x%04x, at table pos. %lu\n",
0094 cmd, crc, (crc % ARRAY_SIZE(ftpcmds)));
0095
0096 if (!pdesc->str || strncmp(pdesc->str, cmd, len))
0097 return HMCDRV_FTP_NOOP;
0098
0099 pr_debug("FTP command '%s' found, with ID %d\n",
0100 pdesc->str, pdesc->cmd);
0101
0102 return pdesc->cmd;
0103 }
0104
0105
0106
0107
0108
0109
0110
0111
0112 static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp)
0113 {
0114 char *start;
0115 int argc = 0;
0116
0117 ftp->id = HMCDRV_FTP_NOOP;
0118 ftp->fname = NULL;
0119
0120 while (*cmd != '\0') {
0121
0122 while (isspace(*cmd))
0123 ++cmd;
0124
0125 if (*cmd == '\0')
0126 break;
0127
0128 start = cmd;
0129
0130 switch (argc) {
0131 case 0:
0132 while ((*cmd != '\0') && !isspace(*cmd))
0133 ++cmd;
0134 ftp->id = hmcdrv_ftp_cmd_getid(start, cmd - start);
0135 break;
0136 case 1:
0137 while ((*cmd != '\0') && !iscntrl(*cmd))
0138 ++cmd;
0139 ftp->fname = start;
0140 fallthrough;
0141 default:
0142 *cmd = '\0';
0143 break;
0144 }
0145
0146 ++argc;
0147 }
0148
0149 if (!ftp->fname || (ftp->id == HMCDRV_FTP_NOOP))
0150 return -EINVAL;
0151
0152 return 0;
0153 }
0154
0155
0156
0157
0158
0159
0160
0161 ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp)
0162 {
0163 ssize_t len;
0164
0165 mutex_lock(&hmcdrv_ftp_mutex);
0166
0167 if (hmcdrv_ftp_funcs && hmcdrv_ftp_refcnt) {
0168 pr_debug("starting transfer, cmd %d for '%s' at %lld with %zd bytes\n",
0169 ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len);
0170 len = hmcdrv_cache_cmd(ftp, hmcdrv_ftp_funcs->transfer);
0171 } else {
0172 len = -ENXIO;
0173 }
0174
0175 mutex_unlock(&hmcdrv_ftp_mutex);
0176 return len;
0177 }
0178 EXPORT_SYMBOL(hmcdrv_ftp_do);
0179
0180
0181
0182
0183
0184
0185 int hmcdrv_ftp_probe(void)
0186 {
0187 int rc;
0188
0189 struct hmcdrv_ftp_cmdspec ftp = {
0190 .id = HMCDRV_FTP_NOOP,
0191 .ofs = 0,
0192 .fname = "",
0193 .len = PAGE_SIZE
0194 };
0195
0196 ftp.buf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
0197
0198 if (!ftp.buf)
0199 return -ENOMEM;
0200
0201 rc = hmcdrv_ftp_startup();
0202
0203 if (rc)
0204 goto out;
0205
0206 rc = hmcdrv_ftp_do(&ftp);
0207 hmcdrv_ftp_shutdown();
0208
0209 switch (rc) {
0210 case -ENOENT:
0211 case -EBUSY:
0212 rc = 0;
0213 break;
0214 default:
0215 if (rc > 0)
0216 rc = 0;
0217 break;
0218 }
0219 out:
0220 free_page((unsigned long) ftp.buf);
0221 return rc;
0222 }
0223 EXPORT_SYMBOL(hmcdrv_ftp_probe);
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234
0235
0236
0237 ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset,
0238 char __user *buf, size_t len)
0239 {
0240 int order;
0241
0242 struct hmcdrv_ftp_cmdspec ftp = {.len = len, .ofs = offset};
0243 ssize_t retlen = hmcdrv_ftp_parse(cmd, &ftp);
0244
0245 if (retlen)
0246 return retlen;
0247
0248 order = get_order(ftp.len);
0249 ftp.buf = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, order);
0250
0251 if (!ftp.buf)
0252 return -ENOMEM;
0253
0254 switch (ftp.id) {
0255 case HMCDRV_FTP_DIR:
0256 case HMCDRV_FTP_NLIST:
0257 case HMCDRV_FTP_GET:
0258 retlen = hmcdrv_ftp_do(&ftp);
0259
0260 if ((retlen >= 0) &&
0261 copy_to_user(buf, ftp.buf, retlen))
0262 retlen = -EFAULT;
0263 break;
0264
0265 case HMCDRV_FTP_PUT:
0266 case HMCDRV_FTP_APPEND:
0267 if (!copy_from_user(ftp.buf, buf, ftp.len))
0268 retlen = hmcdrv_ftp_do(&ftp);
0269 else
0270 retlen = -EFAULT;
0271 break;
0272
0273 case HMCDRV_FTP_DELETE:
0274 retlen = hmcdrv_ftp_do(&ftp);
0275 break;
0276
0277 default:
0278 retlen = -EOPNOTSUPP;
0279 break;
0280 }
0281
0282 free_pages((unsigned long) ftp.buf, order);
0283 return retlen;
0284 }
0285
0286
0287
0288
0289
0290
0291
0292 int hmcdrv_ftp_startup(void)
0293 {
0294 static const struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = {
0295 .startup = diag_ftp_startup,
0296 .shutdown = diag_ftp_shutdown,
0297 .transfer = diag_ftp_cmd
0298 };
0299
0300 static const struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = {
0301 .startup = sclp_ftp_startup,
0302 .shutdown = sclp_ftp_shutdown,
0303 .transfer = sclp_ftp_cmd
0304 };
0305
0306 int rc = 0;
0307
0308 mutex_lock(&hmcdrv_ftp_mutex);
0309
0310 if (hmcdrv_ftp_refcnt == 0) {
0311 if (MACHINE_IS_VM)
0312 hmcdrv_ftp_funcs = &hmcdrv_ftp_zvm;
0313 else if (MACHINE_IS_LPAR || MACHINE_IS_KVM)
0314 hmcdrv_ftp_funcs = &hmcdrv_ftp_lpar;
0315 else
0316 rc = -EOPNOTSUPP;
0317
0318 if (hmcdrv_ftp_funcs)
0319 rc = hmcdrv_ftp_funcs->startup();
0320 }
0321
0322 if (!rc)
0323 ++hmcdrv_ftp_refcnt;
0324
0325 mutex_unlock(&hmcdrv_ftp_mutex);
0326 return rc;
0327 }
0328 EXPORT_SYMBOL(hmcdrv_ftp_startup);
0329
0330
0331
0332
0333
0334 void hmcdrv_ftp_shutdown(void)
0335 {
0336 mutex_lock(&hmcdrv_ftp_mutex);
0337 --hmcdrv_ftp_refcnt;
0338
0339 if ((hmcdrv_ftp_refcnt == 0) && hmcdrv_ftp_funcs)
0340 hmcdrv_ftp_funcs->shutdown();
0341
0342 mutex_unlock(&hmcdrv_ftp_mutex);
0343 }
0344 EXPORT_SYMBOL(hmcdrv_ftp_shutdown);