Commit | Line | Data |
---|---|---|
8f933b10 RH |
1 | /* |
2 | * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR | |
3 | * | |
4 | * Copyright IBM Corp. 2013 | |
5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | |
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 | ||
26 | static DECLARE_COMPLETION(sclp_ftp_rx_complete); | |
27 | static u8 sclp_ftp_ldflg; | |
28 | static u64 sclp_ftp_fsize; | |
29 | static u64 sclp_ftp_length; | |
30 | ||
31 | /** | |
32 | * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback | |
33 | */ | |
34 | static 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 | * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback | |
47 | */ | |
48 | static void sclp_ftp_rxcb(struct evbuf_header *evbuf) | |
49 | { | |
50 | struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf; | |
51 | ||
52 | /* | |
53 | * Check for Diagnostic Test FTP Service | |
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 | * Because the event buffer is located in a page which is owned | |
68 | * by the SCLP core, all data of interest must be copied. The | |
69 | * error indication is in 'sclp_ftp_ldflg' | |
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 | * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request | |
80 | * @ftp: pointer to FTP descriptor | |
81 | * | |
82 | * Return: 0 on success, else a (negative) error code | |
83 | */ | |
84 | static 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; /* clear processed-buffer */ | |
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 | /* Wait for end of ftp sclp command. */ | |
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 | * Check if sclp accepted the request. The data transfer runs | |
145 | * asynchronously and the completion is indicated with an | |
146 | * sclp ET7 event. | |
147 | */ | |
148 | if (req->status != SCLP_REQ_DONE || | |
149 | (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */ | |
150 | (sccb->hdr.response_code & 0xffU) != 0x20U) { | |
151 | rc = -EIO; | |
152 | } | |
153 | ||
154 | out_free: | |
155 | free_page((unsigned long) sccb); | |
156 | kfree(req); | |
157 | return rc; | |
158 | } | |
159 | ||
160 | /** | |
161 | * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command | |
162 | * @ftp: pointer to FTP command specification | |
163 | * @fsize: return of file size (or NULL if undesirable) | |
164 | * | |
165 | * Attention: Notice that this function is not reentrant - so the caller | |
166 | * must ensure locking. | |
167 | * | |
168 | * Return: number of bytes read/written or a (negative) error code | |
169 | */ | |
170 | ssize_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 | /* Start ftp sclp command. */ | |
184 | len = sclp_ftp_et7(ftp); | |
185 | if (len) | |
186 | goto out_unlock; | |
187 | ||
188 | /* | |
189 | * There is no way to cancel the sclp ET7 request, the code | |
190 | * needs to wait unconditionally until the transfer is complete. | |
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 | ||
221 | out_unlock: | |
222 | return len; | |
223 | } | |
224 | ||
225 | /* | |
226 | * ET7 event listener | |
227 | */ | |
228 | static struct sclp_register sclp_ftp_event = { | |
229 | .send_mask = EVTYP_DIAG_TEST_MASK, /* want tx events */ | |
230 | .receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */ | |
231 | .receiver_fn = sclp_ftp_rxcb, /* async callback (rx) */ | |
232 | .state_change_fn = NULL, | |
233 | .pm_event_fn = NULL, | |
234 | }; | |
235 | ||
236 | /** | |
237 | * sclp_ftp_startup() - startup of FTP services, when running on LPAR | |
238 | */ | |
239 | int 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)) { /* get SYSIB 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 /* DEBUG */ | |
266 | return 0; | |
267 | } | |
268 | ||
269 | /** | |
270 | * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR | |
271 | */ | |
272 | void sclp_ftp_shutdown(void) | |
273 | { | |
274 | sclp_unregister(&sclp_ftp_event); | |
275 | } |