1
2
3
4
5
6
7
8
9#define KMSG_COMPONENT "hmcdrv"
10#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11
12#include <linux/kernel.h>
13#include <linux/mm.h>
14#include <linux/slab.h>
15#include <linux/io.h>
16#include <linux/wait.h>
17#include <linux/string.h>
18#include <linux/jiffies.h>
19#include <asm/sysinfo.h>
20#include <asm/ebcdic.h>
21
22#include "sclp.h"
23#include "sclp_diag.h"
24#include "sclp_ftp.h"
25
26static DECLARE_COMPLETION(sclp_ftp_rx_complete);
27static u8 sclp_ftp_ldflg;
28static u64 sclp_ftp_fsize;
29static u64 sclp_ftp_length;
30
31
32
33
34static void sclp_ftp_txcb(struct sclp_req *req, void *data)
35{
36 struct completion *completion = data;
37
38#ifdef DEBUG
39 pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n",
40 req->sccb, 24, req->sccb);
41#endif
42 complete(completion);
43}
44
45
46
47
48static void sclp_ftp_rxcb(struct evbuf_header *evbuf)
49{
50 struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf;
51
52
53
54
55 if (evbuf->type != EVTYP_DIAG_TEST ||
56 diag->route != SCLP_DIAG_FTP_ROUTE ||
57 diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX ||
58 evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN)
59 return;
60
61#ifdef DEBUG
62 pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n",
63 evbuf, 24, evbuf);
64#endif
65
66
67
68
69
70
71 sclp_ftp_ldflg = diag->mdd.ftp.ldflg;
72 sclp_ftp_fsize = diag->mdd.ftp.fsize;
73 sclp_ftp_length = diag->mdd.ftp.length;
74
75 complete(&sclp_ftp_rx_complete);
76}
77
78
79
80
81
82
83
84static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp)
85{
86 struct completion completion;
87 struct sclp_diag_sccb *sccb;
88 struct sclp_req *req;
89 size_t len;
90 int rc;
91
92 req = kzalloc(sizeof(*req), GFP_KERNEL);
93 sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
94 if (!req || !sccb) {
95 rc = -ENOMEM;
96 goto out_free;
97 }
98
99 sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN +
100 sizeof(struct sccb_header);
101 sccb->evbuf.hdr.type = EVTYP_DIAG_TEST;
102 sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN;
103 sccb->evbuf.hdr.flags = 0;
104 sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE;
105 sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX;
106 sccb->evbuf.mdd.ftp.srcflg = 0;
107 sccb->evbuf.mdd.ftp.pgsize = 0;
108 sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE;
109 sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL;
110 sccb->evbuf.mdd.ftp.fsize = 0;
111 sccb->evbuf.mdd.ftp.cmd = ftp->id;
112 sccb->evbuf.mdd.ftp.offset = ftp->ofs;
113 sccb->evbuf.mdd.ftp.length = ftp->len;
114 sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf);
115
116 len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname,
117 HMCDRV_FTP_FIDENT_MAX);
118 if (len >= HMCDRV_FTP_FIDENT_MAX) {
119 rc = -EINVAL;
120 goto out_free;
121 }
122
123 req->command = SCLP_CMDW_WRITE_EVENT_DATA;
124 req->sccb = sccb;
125 req->status = SCLP_REQ_FILLED;
126 req->callback = sclp_ftp_txcb;
127 req->callback_data = &completion;
128
129 init_completion(&completion);
130
131 rc = sclp_add_request(req);
132 if (rc)
133 goto out_free;
134
135
136 wait_for_completion(&completion);
137
138#ifdef DEBUG
139 pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n",
140 sccb->hdr.response_code, sccb->evbuf.hdr.flags);
141#endif
142
143
144
145
146
147
148 if (req->status != SCLP_REQ_DONE ||
149 (sccb->evbuf.hdr.flags & 0x80) == 0 ||
150 (sccb->hdr.response_code & 0xffU) != 0x20U) {
151 rc = -EIO;
152 }
153
154out_free:
155 free_page((unsigned long) sccb);
156 kfree(req);
157 return rc;
158}
159
160
161
162
163
164
165
166
167
168
169
170ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize)
171{
172 ssize_t len;
173#ifdef DEBUG
174 unsigned long start_jiffies;
175
176 pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n",
177 ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len);
178 start_jiffies = jiffies;
179#endif
180
181 init_completion(&sclp_ftp_rx_complete);
182
183
184 len = sclp_ftp_et7(ftp);
185 if (len)
186 goto out_unlock;
187
188
189
190
191
192 wait_for_completion(&sclp_ftp_rx_complete);
193
194#ifdef DEBUG
195 pr_debug("completed SCLP (ET7) request after %lu ms (all)\n",
196 (jiffies - start_jiffies) * 1000 / HZ);
197 pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n",
198 sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize);
199#endif
200
201 switch (sclp_ftp_ldflg) {
202 case SCLP_DIAG_FTP_OK:
203 len = sclp_ftp_length;
204 if (fsize)
205 *fsize = sclp_ftp_fsize;
206 break;
207 case SCLP_DIAG_FTP_LDNPERM:
208 len = -EPERM;
209 break;
210 case SCLP_DIAG_FTP_LDRUNS:
211 len = -EBUSY;
212 break;
213 case SCLP_DIAG_FTP_LDFAIL:
214 len = -ENOENT;
215 break;
216 default:
217 len = -EIO;
218 break;
219 }
220
221out_unlock:
222 return len;
223}
224
225
226
227
228static struct sclp_register sclp_ftp_event = {
229 .send_mask = EVTYP_DIAG_TEST_MASK,
230 .receive_mask = EVTYP_DIAG_TEST_MASK,
231 .receiver_fn = sclp_ftp_rxcb,
232 .state_change_fn = NULL,
233 .pm_event_fn = NULL,
234};
235
236
237
238
239int sclp_ftp_startup(void)
240{
241#ifdef DEBUG
242 unsigned long info;
243#endif
244 int rc;
245
246 rc = sclp_register(&sclp_ftp_event);
247 if (rc)
248 return rc;
249
250#ifdef DEBUG
251 info = get_zeroed_page(GFP_KERNEL);
252
253 if (info != 0) {
254 struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info;
255
256 if (!stsi(info222, 2, 2, 2)) {
257 info222->name[sizeof(info222->name) - 1] = '\0';
258 EBCASC_500(info222->name, sizeof(info222->name) - 1);
259 pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n",
260 info222->lpar_number, info222->name);
261 }
262
263 free_page(info);
264 }
265#endif
266 return 0;
267}
268
269
270
271
272void sclp_ftp_shutdown(void)
273{
274 sclp_unregister(&sclp_ftp_event);
275}
276