diff options
author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2016-01-13 06:54:28 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2016-03-07 10:54:32 -0500 |
commit | 988b86e69ded17f0f1209fd3ef1c4c7f1567dcc1 (patch) | |
tree | 7ef6e69d88c827190cc5f66f417efc04f0d03d6f | |
parent | baebc70a4db86515d55ff1f226088a8e7f5821a0 (diff) |
s390/pci: add ioctl interface for CLP
Provide a user space interface to issue call logical-processor instructions.
Only selected CLP commands are allowed, enough to get the full overview of
the installed PCI functions.
Reviewed-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r-- | arch/s390/include/asm/clp.h | 27 | ||||
-rw-r--r-- | arch/s390/include/asm/pci_clp.h | 30 | ||||
-rw-r--r-- | arch/s390/include/uapi/asm/clp.h | 28 | ||||
-rw-r--r-- | arch/s390/pci/pci_clp.c | 247 |
4 files changed, 293 insertions, 39 deletions
diff --git a/arch/s390/include/asm/clp.h b/arch/s390/include/asm/clp.h index a0e71a501f7c..5687d62fb0cb 100644 --- a/arch/s390/include/asm/clp.h +++ b/arch/s390/include/asm/clp.h | |||
@@ -4,14 +4,23 @@ | |||
4 | /* CLP common request & response block size */ | 4 | /* CLP common request & response block size */ |
5 | #define CLP_BLK_SIZE PAGE_SIZE | 5 | #define CLP_BLK_SIZE PAGE_SIZE |
6 | 6 | ||
7 | #define CLP_LPS_BASE 0 | ||
8 | #define CLP_LPS_PCI 2 | ||
9 | |||
7 | struct clp_req_hdr { | 10 | struct clp_req_hdr { |
8 | u16 len; | 11 | u16 len; |
9 | u16 cmd; | 12 | u16 cmd; |
13 | u32 fmt : 4; | ||
14 | u32 reserved1 : 28; | ||
15 | u64 reserved2; | ||
10 | } __packed; | 16 | } __packed; |
11 | 17 | ||
12 | struct clp_rsp_hdr { | 18 | struct clp_rsp_hdr { |
13 | u16 len; | 19 | u16 len; |
14 | u16 rsp; | 20 | u16 rsp; |
21 | u32 fmt : 4; | ||
22 | u32 reserved1 : 28; | ||
23 | u64 reserved2; | ||
15 | } __packed; | 24 | } __packed; |
16 | 25 | ||
17 | /* CLP Response Codes */ | 26 | /* CLP Response Codes */ |
@@ -25,4 +34,22 @@ struct clp_rsp_hdr { | |||
25 | #define CLP_RC_NODATA 0x0080 /* No data available */ | 34 | #define CLP_RC_NODATA 0x0080 /* No data available */ |
26 | #define CLP_RC_FC_UNKNOWN 0x0100 /* Function code not recognized */ | 35 | #define CLP_RC_FC_UNKNOWN 0x0100 /* Function code not recognized */ |
27 | 36 | ||
37 | /* Store logical-processor characteristics request */ | ||
38 | struct clp_req_slpc { | ||
39 | struct clp_req_hdr hdr; | ||
40 | } __packed; | ||
41 | |||
42 | struct clp_rsp_slpc { | ||
43 | struct clp_rsp_hdr hdr; | ||
44 | u32 reserved2[4]; | ||
45 | u32 lpif[8]; | ||
46 | u32 reserved3[8]; | ||
47 | u32 lpic[8]; | ||
48 | } __packed; | ||
49 | |||
50 | struct clp_req_rsp_slpc { | ||
51 | struct clp_req_slpc request; | ||
52 | struct clp_rsp_slpc response; | ||
53 | } __packed; | ||
54 | |||
28 | #endif | 55 | #endif |
diff --git a/arch/s390/include/asm/pci_clp.h b/arch/s390/include/asm/pci_clp.h index dd78f92f1cce..e75c64cbcf08 100644 --- a/arch/s390/include/asm/pci_clp.h +++ b/arch/s390/include/asm/pci_clp.h | |||
@@ -49,9 +49,6 @@ struct clp_fh_list_entry { | |||
49 | /* List PCI functions request */ | 49 | /* List PCI functions request */ |
50 | struct clp_req_list_pci { | 50 | struct clp_req_list_pci { |
51 | struct clp_req_hdr hdr; | 51 | struct clp_req_hdr hdr; |
52 | u32 fmt : 4; /* cmd request block format */ | ||
53 | u32 : 28; | ||
54 | u64 reserved1; | ||
55 | u64 resume_token; | 52 | u64 resume_token; |
56 | u64 reserved2; | 53 | u64 reserved2; |
57 | } __packed; | 54 | } __packed; |
@@ -59,9 +56,6 @@ struct clp_req_list_pci { | |||
59 | /* List PCI functions response */ | 56 | /* List PCI functions response */ |
60 | struct clp_rsp_list_pci { | 57 | struct clp_rsp_list_pci { |
61 | struct clp_rsp_hdr hdr; | 58 | struct clp_rsp_hdr hdr; |
62 | u32 fmt : 4; /* cmd request block format */ | ||
63 | u32 : 28; | ||
64 | u64 reserved1; | ||
65 | u64 resume_token; | 59 | u64 resume_token; |
66 | u32 reserved2; | 60 | u32 reserved2; |
67 | u16 max_fn; | 61 | u16 max_fn; |
@@ -73,9 +67,6 @@ struct clp_rsp_list_pci { | |||
73 | /* Query PCI function request */ | 67 | /* Query PCI function request */ |
74 | struct clp_req_query_pci { | 68 | struct clp_req_query_pci { |
75 | struct clp_req_hdr hdr; | 69 | struct clp_req_hdr hdr; |
76 | u32 fmt : 4; /* cmd request block format */ | ||
77 | u32 : 28; | ||
78 | u64 reserved1; | ||
79 | u32 fh; /* function handle */ | 70 | u32 fh; /* function handle */ |
80 | u32 reserved2; | 71 | u32 reserved2; |
81 | u64 reserved3; | 72 | u64 reserved3; |
@@ -84,9 +75,6 @@ struct clp_req_query_pci { | |||
84 | /* Query PCI function response */ | 75 | /* Query PCI function response */ |
85 | struct clp_rsp_query_pci { | 76 | struct clp_rsp_query_pci { |
86 | struct clp_rsp_hdr hdr; | 77 | struct clp_rsp_hdr hdr; |
87 | u32 fmt : 4; /* cmd request block format */ | ||
88 | u32 : 28; | ||
89 | u64 : 64; | ||
90 | u16 vfn; /* virtual fn number */ | 78 | u16 vfn; /* virtual fn number */ |
91 | u16 : 7; | 79 | u16 : 7; |
92 | u16 util_str_avail : 1; /* utility string available? */ | 80 | u16 util_str_avail : 1; /* utility string available? */ |
@@ -108,21 +96,15 @@ struct clp_rsp_query_pci { | |||
108 | /* Query PCI function group request */ | 96 | /* Query PCI function group request */ |
109 | struct clp_req_query_pci_grp { | 97 | struct clp_req_query_pci_grp { |
110 | struct clp_req_hdr hdr; | 98 | struct clp_req_hdr hdr; |
111 | u32 fmt : 4; /* cmd request block format */ | 99 | u32 reserved2 : 24; |
112 | u32 : 28; | ||
113 | u64 reserved1; | ||
114 | u32 : 24; | ||
115 | u32 pfgid : 8; /* function group id */ | 100 | u32 pfgid : 8; /* function group id */ |
116 | u32 reserved2; | 101 | u32 reserved3; |
117 | u64 reserved3; | 102 | u64 reserved4; |
118 | } __packed; | 103 | } __packed; |
119 | 104 | ||
120 | /* Query PCI function group response */ | 105 | /* Query PCI function group response */ |
121 | struct clp_rsp_query_pci_grp { | 106 | struct clp_rsp_query_pci_grp { |
122 | struct clp_rsp_hdr hdr; | 107 | struct clp_rsp_hdr hdr; |
123 | u32 fmt : 4; /* cmd request block format */ | ||
124 | u32 : 28; | ||
125 | u64 reserved1; | ||
126 | u16 : 4; | 108 | u16 : 4; |
127 | u16 noi : 12; /* number of interrupts */ | 109 | u16 noi : 12; /* number of interrupts */ |
128 | u8 version; | 110 | u8 version; |
@@ -141,9 +123,6 @@ struct clp_rsp_query_pci_grp { | |||
141 | /* Set PCI function request */ | 123 | /* Set PCI function request */ |
142 | struct clp_req_set_pci { | 124 | struct clp_req_set_pci { |
143 | struct clp_req_hdr hdr; | 125 | struct clp_req_hdr hdr; |
144 | u32 fmt : 4; /* cmd request block format */ | ||
145 | u32 : 28; | ||
146 | u64 reserved1; | ||
147 | u32 fh; /* function handle */ | 126 | u32 fh; /* function handle */ |
148 | u16 reserved2; | 127 | u16 reserved2; |
149 | u8 oc; /* operation controls */ | 128 | u8 oc; /* operation controls */ |
@@ -154,9 +133,6 @@ struct clp_req_set_pci { | |||
154 | /* Set PCI function response */ | 133 | /* Set PCI function response */ |
155 | struct clp_rsp_set_pci { | 134 | struct clp_rsp_set_pci { |
156 | struct clp_rsp_hdr hdr; | 135 | struct clp_rsp_hdr hdr; |
157 | u32 fmt : 4; /* cmd request block format */ | ||
158 | u32 : 28; | ||
159 | u64 reserved1; | ||
160 | u32 fh; /* function handle */ | 136 | u32 fh; /* function handle */ |
161 | u32 reserved3; | 137 | u32 reserved3; |
162 | u64 reserved4; | 138 | u64 reserved4; |
diff --git a/arch/s390/include/uapi/asm/clp.h b/arch/s390/include/uapi/asm/clp.h new file mode 100644 index 000000000000..ab72d9d24373 --- /dev/null +++ b/arch/s390/include/uapi/asm/clp.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * ioctl interface for /dev/clp | ||
3 | * | ||
4 | * Copyright IBM Corp. 2016 | ||
5 | * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
6 | */ | ||
7 | |||
8 | #ifndef _ASM_CLP_H | ||
9 | #define _ASM_CLP_H | ||
10 | |||
11 | #include <linux/types.h> | ||
12 | #include <linux/ioctl.h> | ||
13 | |||
14 | struct clp_req { | ||
15 | unsigned int c : 1; | ||
16 | unsigned int r : 1; | ||
17 | unsigned int lps : 6; | ||
18 | unsigned int cmd : 8; | ||
19 | unsigned int : 16; | ||
20 | unsigned int reserved; | ||
21 | __u64 data_p; | ||
22 | }; | ||
23 | |||
24 | #define CLP_IOCTL_MAGIC 'c' | ||
25 | |||
26 | #define CLP_SYNC _IOWR(CLP_IOCTL_MAGIC, 0xC1, struct clp_req) | ||
27 | |||
28 | #endif | ||
diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c index d6e411ed8b1f..21591ddb4c1f 100644 --- a/arch/s390/pci/pci_clp.c +++ b/arch/s390/pci/pci_clp.c | |||
@@ -8,13 +8,19 @@ | |||
8 | #define KMSG_COMPONENT "zpci" | 8 | #define KMSG_COMPONENT "zpci" |
9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | 9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
10 | 10 | ||
11 | #include <linux/compat.h> | ||
11 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/miscdevice.h> | ||
12 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
13 | #include <linux/err.h> | 15 | #include <linux/err.h> |
14 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
15 | #include <linux/pci.h> | 17 | #include <linux/pci.h> |
18 | #include <linux/uaccess.h> | ||
16 | #include <asm/pci_debug.h> | 19 | #include <asm/pci_debug.h> |
17 | #include <asm/pci_clp.h> | 20 | #include <asm/pci_clp.h> |
21 | #include <asm/compat.h> | ||
22 | #include <asm/clp.h> | ||
23 | #include <uapi/asm/clp.h> | ||
18 | 24 | ||
19 | static inline void zpci_err_clp(unsigned int rsp, int rc) | 25 | static inline void zpci_err_clp(unsigned int rsp, int rc) |
20 | { | 26 | { |
@@ -27,21 +33,43 @@ static inline void zpci_err_clp(unsigned int rsp, int rc) | |||
27 | } | 33 | } |
28 | 34 | ||
29 | /* | 35 | /* |
30 | * Call Logical Processor | 36 | * Call Logical Processor with c=1, lps=0 and command 1 |
31 | * Retry logic is handled by the caller. | 37 | * to get the bit mask of installed logical processors |
32 | */ | 38 | */ |
33 | static inline u8 clp_instr(void *data) | 39 | static inline int clp_get_ilp(unsigned long *ilp) |
40 | { | ||
41 | unsigned long mask; | ||
42 | int cc = 3; | ||
43 | |||
44 | asm volatile ( | ||
45 | " .insn rrf,0xb9a00000,%[mask],%[cmd],8,0\n" | ||
46 | "0: ipm %[cc]\n" | ||
47 | " srl %[cc],28\n" | ||
48 | "1:\n" | ||
49 | EX_TABLE(0b, 1b) | ||
50 | : [cc] "+d" (cc), [mask] "=d" (mask) : [cmd] "a" (1) | ||
51 | : "cc"); | ||
52 | *ilp = mask; | ||
53 | return cc; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Call Logical Processor with c=0, the give constant lps and an lpcb request. | ||
58 | */ | ||
59 | static inline int clp_req(void *data, unsigned int lps) | ||
34 | { | 60 | { |
35 | struct { u8 _[CLP_BLK_SIZE]; } *req = data; | 61 | struct { u8 _[CLP_BLK_SIZE]; } *req = data; |
36 | u64 ignored; | 62 | u64 ignored; |
37 | u8 cc; | 63 | int cc = 3; |
38 | 64 | ||
39 | asm volatile ( | 65 | asm volatile ( |
40 | " .insn rrf,0xb9a00000,%[ign],%[req],0x0,0x2\n" | 66 | " .insn rrf,0xb9a00000,%[ign],%[req],0,%[lps]\n" |
41 | " ipm %[cc]\n" | 67 | "0: ipm %[cc]\n" |
42 | " srl %[cc],28\n" | 68 | " srl %[cc],28\n" |
43 | : [cc] "=d" (cc), [ign] "=d" (ignored), "+m" (*req) | 69 | "1:\n" |
44 | : [req] "a" (req) | 70 | EX_TABLE(0b, 1b) |
71 | : [cc] "+d" (cc), [ign] "=d" (ignored), "+m" (*req) | ||
72 | : [req] "a" (req), [lps] "i" (lps) | ||
45 | : "cc"); | 73 | : "cc"); |
46 | return cc; | 74 | return cc; |
47 | } | 75 | } |
@@ -90,7 +118,7 @@ static int clp_query_pci_fngrp(struct zpci_dev *zdev, u8 pfgid) | |||
90 | rrb->response.hdr.len = sizeof(rrb->response); | 118 | rrb->response.hdr.len = sizeof(rrb->response); |
91 | rrb->request.pfgid = pfgid; | 119 | rrb->request.pfgid = pfgid; |
92 | 120 | ||
93 | rc = clp_instr(rrb); | 121 | rc = clp_req(rrb, CLP_LPS_PCI); |
94 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) | 122 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) |
95 | clp_store_query_pci_fngrp(zdev, &rrb->response); | 123 | clp_store_query_pci_fngrp(zdev, &rrb->response); |
96 | else { | 124 | else { |
@@ -143,7 +171,7 @@ static int clp_query_pci_fn(struct zpci_dev *zdev, u32 fh) | |||
143 | rrb->response.hdr.len = sizeof(rrb->response); | 171 | rrb->response.hdr.len = sizeof(rrb->response); |
144 | rrb->request.fh = fh; | 172 | rrb->request.fh = fh; |
145 | 173 | ||
146 | rc = clp_instr(rrb); | 174 | rc = clp_req(rrb, CLP_LPS_PCI); |
147 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) { | 175 | if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) { |
148 | rc = clp_store_query_pci_fn(zdev, &rrb->response); | 176 | rc = clp_store_query_pci_fn(zdev, &rrb->response); |
149 | if (rc) | 177 | if (rc) |
@@ -214,7 +242,7 @@ static int clp_set_pci_fn(u32 *fh, u8 nr_dma_as, u8 command) | |||
214 | rrb->request.oc = command; | 242 | rrb->request.oc = command; |
215 | rrb->request.ndas = nr_dma_as; | 243 | rrb->request.ndas = nr_dma_as; |
216 | 244 | ||
217 | rc = clp_instr(rrb); | 245 | rc = clp_req(rrb, CLP_LPS_PCI); |
218 | if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) { | 246 | if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) { |
219 | retries--; | 247 | retries--; |
220 | if (retries < 0) | 248 | if (retries < 0) |
@@ -280,7 +308,7 @@ static int clp_list_pci(struct clp_req_rsp_list_pci *rrb, | |||
280 | rrb->request.resume_token = resume_token; | 308 | rrb->request.resume_token = resume_token; |
281 | 309 | ||
282 | /* Get PCI function handle list */ | 310 | /* Get PCI function handle list */ |
283 | rc = clp_instr(rrb); | 311 | rc = clp_req(rrb, CLP_LPS_PCI); |
284 | if (rc || rrb->response.hdr.rsp != CLP_RC_OK) { | 312 | if (rc || rrb->response.hdr.rsp != CLP_RC_OK) { |
285 | zpci_err("List PCI FN:\n"); | 313 | zpci_err("List PCI FN:\n"); |
286 | zpci_err_clp(rrb->response.hdr.rsp, rc); | 314 | zpci_err_clp(rrb->response.hdr.rsp, rc); |
@@ -391,3 +419,198 @@ int clp_rescan_pci_devices_simple(void) | |||
391 | clp_free_block(rrb); | 419 | clp_free_block(rrb); |
392 | return rc; | 420 | return rc; |
393 | } | 421 | } |
422 | |||
423 | static int clp_base_slpc(struct clp_req *req, struct clp_req_rsp_slpc *lpcb) | ||
424 | { | ||
425 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | ||
426 | |||
427 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | ||
428 | lpcb->response.hdr.len > limit) | ||
429 | return -EINVAL; | ||
430 | return clp_req(lpcb, CLP_LPS_BASE) ? -EOPNOTSUPP : 0; | ||
431 | } | ||
432 | |||
433 | static int clp_base_command(struct clp_req *req, struct clp_req_hdr *lpcb) | ||
434 | { | ||
435 | switch (lpcb->cmd) { | ||
436 | case 0x0001: /* store logical-processor characteristics */ | ||
437 | return clp_base_slpc(req, (void *) lpcb); | ||
438 | default: | ||
439 | return -EINVAL; | ||
440 | } | ||
441 | } | ||
442 | |||
443 | static int clp_pci_slpc(struct clp_req *req, struct clp_req_rsp_slpc *lpcb) | ||
444 | { | ||
445 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | ||
446 | |||
447 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | ||
448 | lpcb->response.hdr.len > limit) | ||
449 | return -EINVAL; | ||
450 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | ||
451 | } | ||
452 | |||
453 | static int clp_pci_list(struct clp_req *req, struct clp_req_rsp_list_pci *lpcb) | ||
454 | { | ||
455 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | ||
456 | |||
457 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | ||
458 | lpcb->response.hdr.len > limit) | ||
459 | return -EINVAL; | ||
460 | if (lpcb->request.reserved2 != 0) | ||
461 | return -EINVAL; | ||
462 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | ||
463 | } | ||
464 | |||
465 | static int clp_pci_query(struct clp_req *req, | ||
466 | struct clp_req_rsp_query_pci *lpcb) | ||
467 | { | ||
468 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | ||
469 | |||
470 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | ||
471 | lpcb->response.hdr.len > limit) | ||
472 | return -EINVAL; | ||
473 | if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0) | ||
474 | return -EINVAL; | ||
475 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | ||
476 | } | ||
477 | |||
478 | static int clp_pci_query_grp(struct clp_req *req, | ||
479 | struct clp_req_rsp_query_pci_grp *lpcb) | ||
480 | { | ||
481 | unsigned long limit = PAGE_SIZE - sizeof(lpcb->request); | ||
482 | |||
483 | if (lpcb->request.hdr.len != sizeof(lpcb->request) || | ||
484 | lpcb->response.hdr.len > limit) | ||
485 | return -EINVAL; | ||
486 | if (lpcb->request.reserved2 != 0 || lpcb->request.reserved3 != 0 || | ||
487 | lpcb->request.reserved4 != 0) | ||
488 | return -EINVAL; | ||
489 | return clp_req(lpcb, CLP_LPS_PCI) ? -EOPNOTSUPP : 0; | ||
490 | } | ||
491 | |||
492 | static int clp_pci_command(struct clp_req *req, struct clp_req_hdr *lpcb) | ||
493 | { | ||
494 | switch (lpcb->cmd) { | ||
495 | case 0x0001: /* store logical-processor characteristics */ | ||
496 | return clp_pci_slpc(req, (void *) lpcb); | ||
497 | case 0x0002: /* list PCI functions */ | ||
498 | return clp_pci_list(req, (void *) lpcb); | ||
499 | case 0x0003: /* query PCI function */ | ||
500 | return clp_pci_query(req, (void *) lpcb); | ||
501 | case 0x0004: /* query PCI function group */ | ||
502 | return clp_pci_query_grp(req, (void *) lpcb); | ||
503 | default: | ||
504 | return -EINVAL; | ||
505 | } | ||
506 | } | ||
507 | |||
508 | static int clp_normal_command(struct clp_req *req) | ||
509 | { | ||
510 | struct clp_req_hdr *lpcb; | ||
511 | void __user *uptr; | ||
512 | int rc; | ||
513 | |||
514 | rc = -EINVAL; | ||
515 | if (req->lps != 0 && req->lps != 2) | ||
516 | goto out; | ||
517 | |||
518 | rc = -ENOMEM; | ||
519 | lpcb = clp_alloc_block(GFP_KERNEL); | ||
520 | if (!lpcb) | ||
521 | goto out; | ||
522 | |||
523 | rc = -EFAULT; | ||
524 | uptr = (void __force __user *)(unsigned long) req->data_p; | ||
525 | if (copy_from_user(lpcb, uptr, PAGE_SIZE) != 0) | ||
526 | goto out_free; | ||
527 | |||
528 | rc = -EINVAL; | ||
529 | if (lpcb->fmt != 0 || lpcb->reserved1 != 0 || lpcb->reserved2 != 0) | ||
530 | goto out_free; | ||
531 | |||
532 | switch (req->lps) { | ||
533 | case 0: | ||
534 | rc = clp_base_command(req, lpcb); | ||
535 | break; | ||
536 | case 2: | ||
537 | rc = clp_pci_command(req, lpcb); | ||
538 | break; | ||
539 | } | ||
540 | if (rc) | ||
541 | goto out_free; | ||
542 | |||
543 | rc = -EFAULT; | ||
544 | if (copy_to_user(uptr, lpcb, PAGE_SIZE) != 0) | ||
545 | goto out_free; | ||
546 | |||
547 | rc = 0; | ||
548 | |||
549 | out_free: | ||
550 | clp_free_block(lpcb); | ||
551 | out: | ||
552 | return rc; | ||
553 | } | ||
554 | |||
555 | static int clp_immediate_command(struct clp_req *req) | ||
556 | { | ||
557 | void __user *uptr; | ||
558 | unsigned long ilp; | ||
559 | int exists; | ||
560 | |||
561 | if (req->cmd > 1 || clp_get_ilp(&ilp) != 0) | ||
562 | return -EINVAL; | ||
563 | |||
564 | uptr = (void __force __user *)(unsigned long) req->data_p; | ||
565 | if (req->cmd == 0) { | ||
566 | /* Command code 0: test for a specific processor */ | ||
567 | exists = test_bit_inv(req->lps, &ilp); | ||
568 | return put_user(exists, (int __user *) uptr); | ||
569 | } | ||
570 | /* Command code 1: return bit mask of installed processors */ | ||
571 | return put_user(ilp, (unsigned long __user *) uptr); | ||
572 | } | ||
573 | |||
574 | static long clp_misc_ioctl(struct file *filp, unsigned int cmd, | ||
575 | unsigned long arg) | ||
576 | { | ||
577 | struct clp_req req; | ||
578 | void __user *argp; | ||
579 | |||
580 | if (cmd != CLP_SYNC) | ||
581 | return -EINVAL; | ||
582 | |||
583 | argp = is_compat_task() ? compat_ptr(arg) : (void __user *) arg; | ||
584 | if (copy_from_user(&req, argp, sizeof(req))) | ||
585 | return -EFAULT; | ||
586 | if (req.r != 0) | ||
587 | return -EINVAL; | ||
588 | return req.c ? clp_immediate_command(&req) : clp_normal_command(&req); | ||
589 | } | ||
590 | |||
591 | static int clp_misc_release(struct inode *inode, struct file *filp) | ||
592 | { | ||
593 | return 0; | ||
594 | } | ||
595 | |||
596 | static const struct file_operations clp_misc_fops = { | ||
597 | .owner = THIS_MODULE, | ||
598 | .open = nonseekable_open, | ||
599 | .release = clp_misc_release, | ||
600 | .unlocked_ioctl = clp_misc_ioctl, | ||
601 | .compat_ioctl = clp_misc_ioctl, | ||
602 | .llseek = no_llseek, | ||
603 | }; | ||
604 | |||
605 | static struct miscdevice clp_misc_device = { | ||
606 | .minor = MISC_DYNAMIC_MINOR, | ||
607 | .name = "clp", | ||
608 | .fops = &clp_misc_fops, | ||
609 | }; | ||
610 | |||
611 | static int __init clp_misc_init(void) | ||
612 | { | ||
613 | return misc_register(&clp_misc_device); | ||
614 | } | ||
615 | |||
616 | device_initcall(clp_misc_init); | ||