]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
a755a45d JG |
2 | /* |
3 | * Copyright IBM Corp. 2012 | |
4 | * | |
5 | * Author(s): | |
6 | * Jan Glauber <jang@linux.vnet.ibm.com> | |
7 | */ | |
8 | ||
896cb7e6 GS |
9 | #define KMSG_COMPONENT "zpci" |
10 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | |
a755a45d | 11 | |
988b86e6 | 12 | #include <linux/compat.h> |
a755a45d | 13 | #include <linux/kernel.h> |
988b86e6 | 14 | #include <linux/miscdevice.h> |
a755a45d JG |
15 | #include <linux/slab.h> |
16 | #include <linux/err.h> | |
17 | #include <linux/delay.h> | |
18 | #include <linux/pci.h> | |
988b86e6 | 19 | #include <linux/uaccess.h> |
a2ab8333 | 20 | #include <asm/pci_debug.h> |
a755a45d | 21 | #include <asm/pci_clp.h> |
988b86e6 MS |
22 | #include <asm/compat.h> |
23 | #include <asm/clp.h> | |
24 | #include <uapi/asm/clp.h> | |
a755a45d | 25 | |
5c5afd02 SO |
26 | bool zpci_unique_uid; |
27 | ||
5db23179 SO |
28 | static void update_uid_checking(bool new) |
29 | { | |
30 | if (zpci_unique_uid != new) | |
31 | zpci_dbg(1, "uid checking:%d\n", new); | |
32 | ||
33 | zpci_unique_uid = new; | |
34 | } | |
35 | ||
1f1dcbd4 SO |
36 | static inline void zpci_err_clp(unsigned int rsp, int rc) |
37 | { | |
38 | struct { | |
39 | unsigned int rsp; | |
40 | int rc; | |
41 | } __packed data = {rsp, rc}; | |
42 | ||
43 | zpci_err_hex(&data, sizeof(data)); | |
44 | } | |
45 | ||
a755a45d | 46 | /* |
988b86e6 MS |
47 | * Call Logical Processor with c=1, lps=0 and command 1 |
48 | * to get the bit mask of installed logical processors | |
a755a45d | 49 | */ |
988b86e6 MS |
50 | static inline int clp_get_ilp(unsigned long *ilp) |
51 | { | |
52 | unsigned long mask; | |
53 | int cc = 3; | |
54 | ||
55 | asm volatile ( | |
56 | " .insn rrf,0xb9a00000,%[mask],%[cmd],8,0\n" | |
57 | "0: ipm %[cc]\n" | |
58 | " srl %[cc],28\n" | |
59 | "1:\n" | |
60 | EX_TABLE(0b, 1b) | |
61 | : [cc] "+d" (cc), [mask] "=d" (mask) : [cmd] "a" (1) | |
62 | : "cc"); | |
63 | *ilp = mask; | |
64 | return cc; | |
65 | } | |
66 | ||
67 | /* | |
68 | * Call Logical Processor with c=0, the give constant lps and an lpcb request. | |
69 | */ | |
70 | static inline int clp_req(void *data, unsigned int lps) | |
a755a45d | 71 | { |
bf4ec24f SO |
72 | struct { u8 _[CLP_BLK_SIZE]; } *req = data; |
73 | u64 ignored; | |
988b86e6 | 74 | int cc = 3; |
a755a45d JG |
75 | |
76 | asm volatile ( | |
988b86e6 MS |
77 | " .insn rrf,0xb9a00000,%[ign],%[req],0,%[lps]\n" |
78 | "0: ipm %[cc]\n" | |
a755a45d | 79 | " srl %[cc],28\n" |
988b86e6 MS |
80 | "1:\n" |
81 | EX_TABLE(0b, 1b) | |
82 | : [cc] "+d" (cc), [ign] "=d" (ignored), "+m" (*req) | |
83 | : [req] "a" (req), [lps] "i" (lps) | |
bf4ec24f | 84 | : "cc"); |
a755a45d JG |
85 | return cc; |
86 | } | |
87 | ||
1d578966 | 88 | static void *clp_alloc_block(gfp_t gfp_mask) |
a755a45d | 89 | { |
1d578966 | 90 | return (void *) __get_free_pages(gfp_mask, get_order(CLP_BLK_SIZE)); |
a755a45d JG |
91 | } |
92 | ||
93 | static void clp_free_block(void *ptr) | |
94 | { | |
95 | free_pages((unsigned long) ptr, get_order(CLP_BLK_SIZE)); | |
96 | } | |
97 | ||
98 | static void clp_store_query_pci_fngrp(struct zpci_dev *zdev, | |
99 | struct clp_rsp_query_pci_grp *response) | |
100 | { | |
828b35f6 JG |
101 | zdev->tlb_refresh = response->refresh; |
102 | zdev->dma_mask = response->dasm; | |
9a4da8a5 | 103 | zdev->msi_addr = response->msia; |
b19148f6 | 104 | zdev->max_msi = response->noi; |
d0b08853 | 105 | zdev->fmb_update = response->mui; |
9a4da8a5 | 106 | |
a755a45d JG |
107 | switch (response->version) { |
108 | case 1: | |
109 | zdev->max_bus_speed = PCIE_SPEED_5_0GT; | |
110 | break; | |
111 | default: | |
112 | zdev->max_bus_speed = PCI_SPEED_UNKNOWN; | |
113 | break; | |
114 | } | |
115 | } | |
116 | ||
117 | static int clp_query_pci_fngrp(struct zpci_dev *zdev, u8 pfgid) | |
118 | { | |
119 | struct clp_req_rsp_query_pci_grp *rrb; | |
120 | int rc; | |
121 | ||
1d578966 | 122 | rrb = clp_alloc_block(GFP_KERNEL); |
a755a45d JG |
123 | if (!rrb) |
124 | return -ENOMEM; | |
125 | ||
126 | memset(rrb, 0, sizeof(*rrb)); | |
127 | rrb->request.hdr.len = sizeof(rrb->request); | |
128 | rrb->request.hdr.cmd = CLP_QUERY_PCI_FNGRP; | |
129 | rrb->response.hdr.len = sizeof(rrb->response); | |
130 | rrb->request.pfgid = pfgid; | |
131 | ||
988b86e6 | 132 | rc = clp_req(rrb, CLP_LPS_PCI); |
a755a45d JG |
133 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) |
134 | clp_store_query_pci_fngrp(zdev, &rrb->response); | |
135 | else { | |
1f1dcbd4 SO |
136 | zpci_err("Q PCI FGRP:\n"); |
137 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
138 | rc = -EIO; |
139 | } | |
140 | clp_free_block(rrb); | |
141 | return rc; | |
142 | } | |
143 | ||
144 | static int clp_store_query_pci_fn(struct zpci_dev *zdev, | |
145 | struct clp_rsp_query_pci *response) | |
146 | { | |
147 | int i; | |
148 | ||
149 | for (i = 0; i < PCI_BAR_COUNT; i++) { | |
150 | zdev->bars[i].val = le32_to_cpu(response->bar[i]); | |
151 | zdev->bars[i].size = response->bar_size[i]; | |
152 | } | |
828b35f6 JG |
153 | zdev->start_dma = response->sdma; |
154 | zdev->end_dma = response->edma; | |
a755a45d JG |
155 | zdev->pchid = response->pchid; |
156 | zdev->pfgid = response->pfgid; | |
ac4995b9 SO |
157 | zdev->pft = response->pft; |
158 | zdev->vfn = response->vfn; | |
159 | zdev->uid = response->uid; | |
0b7589ec | 160 | zdev->fmb_length = sizeof(u32) * response->fmb_len; |
ac4995b9 SO |
161 | |
162 | memcpy(zdev->pfip, response->pfip, sizeof(zdev->pfip)); | |
163 | if (response->util_str_avail) { | |
164 | memcpy(zdev->util_str, response->util_str, | |
165 | sizeof(zdev->util_str)); | |
166 | } | |
167 | ||
a755a45d JG |
168 | return 0; |
169 | } | |
170 | ||
171 | static int clp_query_pci_fn(struct zpci_dev *zdev, u32 fh) | |
172 | { | |
173 | struct clp_req_rsp_query_pci *rrb; | |
174 | int rc; | |
175 | ||
1d578966 | 176 | rrb = clp_alloc_block(GFP_KERNEL); |
a755a45d JG |
177 | if (!rrb) |
178 | return -ENOMEM; | |
179 | ||
180 | memset(rrb, 0, sizeof(*rrb)); | |
181 | rrb->request.hdr.len = sizeof(rrb->request); | |
182 | rrb->request.hdr.cmd = CLP_QUERY_PCI_FN; | |
183 | rrb->response.hdr.len = sizeof(rrb->response); | |
184 | rrb->request.fh = fh; | |
185 | ||
988b86e6 | 186 | rc = clp_req(rrb, CLP_LPS_PCI); |
a755a45d JG |
187 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) { |
188 | rc = clp_store_query_pci_fn(zdev, &rrb->response); | |
189 | if (rc) | |
190 | goto out; | |
aa624886 | 191 | rc = clp_query_pci_fngrp(zdev, rrb->response.pfgid); |
a755a45d | 192 | } else { |
1f1dcbd4 SO |
193 | zpci_err("Q PCI FN:\n"); |
194 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
195 | rc = -EIO; |
196 | } | |
197 | out: | |
198 | clp_free_block(rrb); | |
199 | return rc; | |
200 | } | |
201 | ||
202 | int clp_add_pci_device(u32 fid, u32 fh, int configured) | |
203 | { | |
204 | struct zpci_dev *zdev; | |
be2c3676 | 205 | int rc = -ENOMEM; |
a755a45d | 206 | |
a2ab8333 | 207 | zpci_dbg(3, "add fid:%x, fh:%x, c:%d\n", fid, fh, configured); |
7d594322 SO |
208 | zdev = kzalloc(sizeof(*zdev), GFP_KERNEL); |
209 | if (!zdev) | |
be2c3676 | 210 | goto error; |
a755a45d JG |
211 | |
212 | zdev->fh = fh; | |
213 | zdev->fid = fid; | |
214 | ||
215 | /* Query function properties and update zdev */ | |
216 | rc = clp_query_pci_fn(zdev, fh); | |
217 | if (rc) | |
218 | goto error; | |
219 | ||
220 | if (configured) | |
221 | zdev->state = ZPCI_FN_STATE_CONFIGURED; | |
222 | else | |
223 | zdev->state = ZPCI_FN_STATE_STANDBY; | |
224 | ||
225 | rc = zpci_create_device(zdev); | |
226 | if (rc) | |
227 | goto error; | |
228 | return 0; | |
229 | ||
230 | error: | |
be2c3676 | 231 | zpci_dbg(0, "add fid:%x, rc:%d\n", fid, rc); |
7d594322 | 232 | kfree(zdev); |
a755a45d JG |
233 | return rc; |
234 | } | |
235 | ||
236 | /* | |
237 | * Enable/Disable a given PCI function defined by its function handle. | |
238 | */ | |
239 | static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command) | |
240 | { | |
241 | struct clp_req_rsp_set_pci *rrb; | |
d03abe58 | 242 | int rc, retries = 100; |
a755a45d | 243 | |
1d578966 | 244 | rrb = clp_alloc_block(GFP_KERNEL); |
a755a45d JG |
245 | if (!rrb) |
246 | return -ENOMEM; | |
247 | ||
248 | do { | |
249 | memset(rrb, 0, sizeof(*rrb)); | |
250 | rrb->request.hdr.len = sizeof(rrb->request); | |
251 | rrb->request.hdr.cmd = CLP_SET_PCI_FN; | |
252 | rrb->response.hdr.len = sizeof(rrb->response); | |
253 | rrb->request.fh = *fh; | |
254 | rrb->request.oc = command; | |
255 | rrb->request.ndas = nr_dma_as; | |
256 | ||
988b86e6 | 257 | rc = clp_req(rrb, CLP_LPS_PCI); |
a755a45d JG |
258 | if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) { |
259 | retries--; | |
260 | if (retries < 0) | |
261 | break; | |
d03abe58 | 262 | msleep(20); |
a755a45d JG |
263 | } |
264 | } while (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY); | |
265 | ||
266 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) | |
267 | *fh = rrb->response.fh; | |
268 | else { | |
1f1dcbd4 SO |
269 | zpci_err("Set PCI FN:\n"); |
270 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
271 | rc = -EIO; |
272 | } | |
273 | clp_free_block(rrb); | |
274 | return rc; | |
275 | } | |
276 | ||
277 | int clp_enable_fh(struct zpci_dev *zdev, u8 nr_dma_as) | |
278 | { | |
279 | u32 fh = zdev->fh; | |
280 | int rc; | |
281 | ||
282 | rc = clp_set_pci_fn(&fh, nr_dma_as, CLP_SET_ENABLE_PCI_FN); | |
283 | if (!rc) | |
284 | /* Success -> store enabled handle in zdev */ | |
285 | zdev->fh = fh; | |
a2ab8333 SO |
286 | |
287 | zpci_dbg(3, "ena fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc); | |
a755a45d JG |
288 | return rc; |
289 | } | |
290 | ||
291 | int clp_disable_fh(struct zpci_dev *zdev) | |
292 | { | |
293 | u32 fh = zdev->fh; | |
294 | int rc; | |
295 | ||
296 | if (!zdev_enabled(zdev)) | |
297 | return 0; | |
298 | ||
a755a45d JG |
299 | rc = clp_set_pci_fn(&fh, 0, CLP_SET_DISABLE_PCI_FN); |
300 | if (!rc) | |
301 | /* Success -> store disabled handle in zdev */ | |
302 | zdev->fh = fh; | |
a2ab8333 SO |
303 | |
304 | zpci_dbg(3, "dis fid:%x, fh:%x, rc:%d\n", zdev->fid, zdev->fh, rc); | |
a755a45d JG |
305 | return rc; |
306 | } | |
307 | ||
783684f1 SO |
308 | static int clp_list_pci(struct clp_req_rsp_list_pci *rrb, void *data, |
309 | void (*cb)(struct clp_fh_list_entry *, void *)) | |
a755a45d | 310 | { |
a755a45d JG |
311 | u64 resume_token = 0; |
312 | int entries, i, rc; | |
313 | ||
a755a45d JG |
314 | do { |
315 | memset(rrb, 0, sizeof(*rrb)); | |
316 | rrb->request.hdr.len = sizeof(rrb->request); | |
317 | rrb->request.hdr.cmd = CLP_LIST_PCI; | |
318 | /* store as many entries as possible */ | |
319 | rrb->response.hdr.len = CLP_BLK_SIZE - LIST_PCI_HDR_LEN; | |
320 | rrb->request.resume_token = resume_token; | |
321 | ||
322 | /* Get PCI function handle list */ | |
988b86e6 | 323 | rc = clp_req(rrb, CLP_LPS_PCI); |
a755a45d | 324 | if (rc || rrb->response.hdr.rsp != CLP_RC_OK) { |
1f1dcbd4 SO |
325 | zpci_err("List PCI FN:\n"); |
326 | zpci_err_clp(rrb->response.hdr.rsp, rc); | |
a755a45d JG |
327 | rc = -EIO; |
328 | goto out; | |
329 | } | |
330 | ||
5db23179 | 331 | update_uid_checking(rrb->response.uid_checking); |
a755a45d JG |
332 | WARN_ON_ONCE(rrb->response.entry_size != |
333 | sizeof(struct clp_fh_list_entry)); | |
334 | ||
335 | entries = (rrb->response.hdr.len - LIST_PCI_HDR_LEN) / | |
336 | rrb->response.entry_size; | |
a755a45d | 337 | |
a755a45d | 338 | resume_token = rrb->response.resume_token; |
a755a45d | 339 | for (i = 0; i < entries; i++) |
783684f1 | 340 | cb(&rrb->response.fh_list[i], data); |
a755a45d | 341 | } while (resume_token); |
a755a45d | 342 | out: |
1d578966 SO |
343 | return rc; |
344 | } | |
345 | ||
783684f1 | 346 | static void __clp_add(struct clp_fh_list_entry *entry, void *data) |
1d578966 SO |
347 | { |
348 | struct zpci_dev *zdev; | |
349 | ||
350 | if (!entry->vendor_id) | |
351 | return; | |
352 | ||
353 | zdev = get_zdev_by_fid(entry->fid); | |
01553d9a | 354 | if (!zdev) |
1d578966 | 355 | clp_add_pci_device(entry->fid, entry->fh, entry->config_state); |
1d578966 SO |
356 | } |
357 | ||
783684f1 | 358 | static void __clp_update(struct clp_fh_list_entry *entry, void *data) |
57b5918c SO |
359 | { |
360 | struct zpci_dev *zdev; | |
361 | ||
362 | if (!entry->vendor_id) | |
363 | return; | |
364 | ||
365 | zdev = get_zdev_by_fid(entry->fid); | |
366 | if (!zdev) | |
367 | return; | |
368 | ||
369 | zdev->fh = entry->fh; | |
370 | } | |
371 | ||
1d578966 SO |
372 | int clp_scan_pci_devices(void) |
373 | { | |
374 | struct clp_req_rsp_list_pci *rrb; | |
375 | int rc; | |
376 | ||
377 | rrb = clp_alloc_block(GFP_KERNEL); | |
378 | if (!rrb) | |
379 | return -ENOMEM; | |
380 | ||
783684f1 | 381 | rc = clp_list_pci(rrb, NULL, __clp_add); |
1d578966 SO |
382 | |
383 | clp_free_block(rrb); | |
384 | return rc; | |
385 | } | |
386 | ||
387 | int clp_rescan_pci_devices(void) | |
388 | { | |
389 | struct clp_req_rsp_list_pci *rrb; | |
390 | int rc; | |
391 | ||
01553d9a SO |
392 | zpci_remove_reserved_devices(); |
393 | ||
1d578966 SO |
394 | rrb = clp_alloc_block(GFP_KERNEL); |
395 | if (!rrb) | |
396 | return -ENOMEM; | |
397 | ||
01553d9a | 398 | rc = clp_list_pci(rrb, NULL, __clp_add); |
1d578966 | 399 | |
a755a45d JG |
400 | clp_free_block(rrb); |
401 | return rc; | |
402 | } | |
57b5918c SO |
403 | |
404 | int clp_rescan_pci_devices_simple(void) | |
405 | { | |
406 | struct clp_req_rsp_list_pci *rrb; | |
407 | int rc; | |
408 | ||
409 | rrb = clp_alloc_block(GFP_NOWAIT); | |
410 | if (!rrb) | |
411 | return -ENOMEM; | |
412 | ||
783684f1 SO |
413 | rc = clp_list_pci(rrb, NULL, __clp_update); |
414 | ||
415 | clp_free_block(rrb); | |
416 | return rc; | |
417 | } | |
418 | ||
419 | struct clp_state_data { | |
420 | u32 fid; | |
421 | enum zpci_state state; | |
422 | }; | |
423 | ||
424 | static void __clp_get_state(struct clp_fh_list_entry *entry, void *data) | |
425 | { | |
426 | struct clp_state_data *sd = data; | |
427 | ||
428 | if (entry->fid != sd->fid) | |
429 | return; | |
430 | ||
431 | sd->state = entry->config_state; | |
432 | } | |
433 | ||
434 | int clp_get_state(u32 fid, enum zpci_state *state) | |
435 | { | |
436 | struct clp_req_rsp_list_pci *rrb; | |
437 | struct clp_state_data sd = {fid, ZPCI_FN_STATE_RESERVED}; | |
438 | int rc; | |
439 | ||
440 | rrb = clp_alloc_block(GFP_KERNEL); | |
441 | if (!rrb) | |
442 | return -ENOMEM; | |
443 | ||
444 | rc = clp_list_pci(rrb, &sd, __clp_get_state); | |
445 | if (!rc) | |
446 | *state = sd.state; | |
57b5918c SO |
447 | |
448 | clp_free_block(rrb); | |
449 | return rc; | |
450 | } | |
988b86e6 MS |
451 | |
452 | static int clp_base_slpc(struct clp_req *req, struct clp_req_rsp_slpc *lpcb) | |
453 | { | |
454 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | |
455 | ||
456 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | |
457 | lpcb->response.hdr.len > limit) | |
458 | return -EINVAL; | |
459 | return clp_req(lpcb, CLP_LPS_BASE) ? -EOPNOTSUPP : 0; | |
460 | } | |
461 | ||
462 | static int clp_base_command(struct clp_req *req, struct clp_req_hdr *lpcb) | |
463 | { | |
464 | switch (lpcb->cmd) { | |
465 | case 0x0001: /* store logical-processor characteristics */ | |
466 | return clp_base_slpc(req, (void *) lpcb); | |
467 | default: | |
468 | return -EINVAL; | |
469 | } | |
470 | } | |
471 | ||
472 | static int clp_pci_slpc(struct clp_req *req, struct clp_req_rsp_slpc *lpcb) | |
473 | { | |
474 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | |
475 | ||
476 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | |
477 | lpcb->response.hdr.len > limit) | |
478 | return -EINVAL; | |
479 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | |
480 | } | |
481 | ||
482 | static int clp_pci_list(struct clp_req *req, struct clp_req_rsp_list_pci *lpcb) | |
483 | { | |
484 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | |
485 | ||
486 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | |
487 | lpcb->response.hdr.len > limit) | |
488 | return -EINVAL; | |
489 | if (lpcb->request.reserved2 != 0) | |
490 | return -EINVAL; | |
491 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | |
492 | } | |
493 | ||
494 | static int clp_pci_query(struct clp_req *req, | |
495 | struct clp_req_rsp_query_pci *lpcb) | |
496 | { | |
497 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | |
498 | ||
499 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | |
500 | lpcb->response.hdr.len > limit) | |
501 | return -EINVAL; | |
502 | if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0) | |
503 | return -EINVAL; | |
504 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | |
505 | } | |
506 | ||
507 | static int clp_pci_query_grp(struct clp_req *req, | |
508 | struct clp_req_rsp_query_pci_grp *lpcb) | |
509 | { | |
510 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | |
511 | ||
512 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | |
513 | lpcb->response.hdr.len > limit) | |
514 | return -EINVAL; | |
515 | if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0 || | |
516 | lpcb->request.reserved4 != 0) | |
517 | return -EINVAL; | |
518 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | |
519 | } | |
520 | ||
521 | static int clp_pci_command(struct clp_req *req, struct clp_req_hdr *lpcb) | |
522 | { | |
523 | switch (lpcb->cmd) { | |
524 | case 0x0001: /* store logical-processor characteristics */ | |
525 | return clp_pci_slpc(req, (void *) lpcb); | |
526 | case 0x0002: /* list PCI functions */ | |
527 | return clp_pci_list(req, (void *) lpcb); | |
528 | case 0x0003: /* query PCI function */ | |
529 | return clp_pci_query(req, (void *) lpcb); | |
530 | case 0x0004: /* query PCI function group */ | |
531 | return clp_pci_query_grp(req, (void *) lpcb); | |
532 | default: | |
533 | return -EINVAL; | |
534 | } | |
535 | } | |
536 | ||
537 | static int clp_normal_command(struct clp_req *req) | |
538 | { | |
539 | struct clp_req_hdr *lpcb; | |
540 | void __user *uptr; | |
541 | int rc; | |
542 | ||
543 | rc = -EINVAL; | |
544 | if (req->lps != 0 && req->lps != 2) | |
545 | goto out; | |
546 | ||
547 | rc = -ENOMEM; | |
548 | lpcb = clp_alloc_block(GFP_KERNEL); | |
549 | if (!lpcb) | |
550 | goto out; | |
551 | ||
552 | rc = -EFAULT; | |
553 | uptr = (void __force __user *)(unsigned long) req->data_p; | |
554 | if (copy_from_user(lpcb, uptr, PAGE_SIZE) != 0) | |
555 | goto out_free; | |
556 | ||
557 | rc = -EINVAL; | |
558 | if (lpcb->fmt != 0 || lpcb->reserved1 != 0 || lpcb->reserved2 != 0) | |
559 | goto out_free; | |
560 | ||
561 | switch (req->lps) { | |
562 | case 0: | |
563 | rc = clp_base_command(req, lpcb); | |
564 | break; | |
565 | case 2: | |
566 | rc = clp_pci_command(req, lpcb); | |
567 | break; | |
568 | } | |
569 | if (rc) | |
570 | goto out_free; | |
571 | ||
572 | rc = -EFAULT; | |
573 | if (copy_to_user(uptr, lpcb, PAGE_SIZE) != 0) | |
574 | goto out_free; | |
575 | ||
576 | rc = 0; | |
577 | ||
578 | out_free: | |
579 | clp_free_block(lpcb); | |
580 | out: | |
581 | return rc; | |
582 | } | |
583 | ||
584 | static int clp_immediate_command(struct clp_req *req) | |
585 | { | |
586 | void __user *uptr; | |
587 | unsigned long ilp; | |
588 | int exists; | |
589 | ||
590 | if (req->cmd > 1 || clp_get_ilp(&ilp) != 0) | |
591 | return -EINVAL; | |
592 | ||
593 | uptr = (void __force __user *)(unsigned long) req->data_p; | |
594 | if (req->cmd == 0) { | |
595 | /* Command code 0: test for a specific processor */ | |
596 | exists = test_bit_inv(req->lps, &ilp); | |
597 | return put_user(exists, (int __user *) uptr); | |
598 | } | |
599 | /* Command code 1: return bit mask of installed processors */ | |
600 | return put_user(ilp, (unsigned long __user *) uptr); | |
601 | } | |
602 | ||
603 | static long clp_misc_ioctl(struct file *filp, unsigned int cmd, | |
604 | unsigned long arg) | |
605 | { | |
606 | struct clp_req req; | |
607 | void __user *argp; | |
608 | ||
609 | if (cmd != CLP_SYNC) | |
610 | return -EINVAL; | |
611 | ||
612 | argp = is_compat_task() ? compat_ptr(arg) : (void __user *) arg; | |
613 | if (copy_from_user(&req, argp, sizeof(req))) | |
614 | return -EFAULT; | |
615 | if (req.r != 0) | |
616 | return -EINVAL; | |
617 | return req.c ? clp_immediate_command(&req) : clp_normal_command(&req); | |
618 | } | |
619 | ||
620 | static int clp_misc_release(struct inode *inode, struct file *filp) | |
621 | { | |
622 | return 0; | |
623 | } | |
624 | ||
625 | static const struct file_operations clp_misc_fops = { | |
626 | .owner = THIS_MODULE, | |
627 | .open = nonseekable_open, | |
628 | .release = clp_misc_release, | |
629 | .unlocked_ioctl = clp_misc_ioctl, | |
630 | .compat_ioctl = clp_misc_ioctl, | |
631 | .llseek = no_llseek, | |
632 | }; | |
633 | ||
634 | static struct miscdevice clp_misc_device = { | |
635 | .minor = MISC_DYNAMIC_MINOR, | |
636 | .name = "clp", | |
637 | .fops = &clp_misc_fops, | |
638 | }; | |
639 | ||
640 | static int __init clp_misc_init(void) | |
641 | { | |
642 | return misc_register(&clp_misc_device); | |
643 | } | |
644 | ||
645 | device_initcall(clp_misc_init); |