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