diff options
author | Christian Borntraeger <cborntra@de.ibm.com> | 2005-06-25 17:55:32 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-06-25 19:24:37 -0400 |
commit | 6b979de395c7e1b7e59f74a870e1d1911853eccb (patch) | |
tree | 82502e9e93c977b0f812d017f5d8d4e12436c6c8 /drivers/s390/char | |
parent | 77fa22450de00d535de2cc8be653983560828000 (diff) |
[PATCH] s390: add vmcp interface
Add interface to issue VM control program commands.
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/s390/char')
-rw-r--r-- | drivers/s390/char/Makefile | 1 | ||||
-rw-r--r-- | drivers/s390/char/con3215.c | 4 | ||||
-rw-r--r-- | drivers/s390/char/con3270.c | 4 | ||||
-rw-r--r-- | drivers/s390/char/vmcp.c | 219 | ||||
-rw-r--r-- | drivers/s390/char/vmcp.h | 30 | ||||
-rw-r--r-- | drivers/s390/char/vmlogrdr.c | 10 |
6 files changed, 259 insertions, 9 deletions
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 14e8cce9f862..6377a96735df 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile | |||
@@ -19,6 +19,7 @@ obj-$(CONFIG_SCLP_CPI) += sclp_cpi.o | |||
19 | 19 | ||
20 | obj-$(CONFIG_ZVM_WATCHDOG) += vmwatchdog.o | 20 | obj-$(CONFIG_ZVM_WATCHDOG) += vmwatchdog.o |
21 | obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o | 21 | obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o |
22 | obj-$(CONFIG_VMCP) += vmcp.o | ||
22 | 23 | ||
23 | tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o | 24 | tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o |
24 | tape-$(CONFIG_PROC_FS) += tape_proc.o | 25 | tape-$(CONFIG_PROC_FS) += tape_proc.o |
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 022f17bff731..f11a67fda40e 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c | |||
@@ -860,8 +860,8 @@ con3215_init(void) | |||
860 | 860 | ||
861 | /* Set the console mode for VM */ | 861 | /* Set the console mode for VM */ |
862 | if (MACHINE_IS_VM) { | 862 | if (MACHINE_IS_VM) { |
863 | cpcmd("TERM CONMODE 3215", NULL, 0); | 863 | cpcmd("TERM CONMODE 3215", NULL, 0, NULL); |
864 | cpcmd("TERM AUTOCR OFF", NULL, 0); | 864 | cpcmd("TERM AUTOCR OFF", NULL, 0, NULL); |
865 | } | 865 | } |
866 | 866 | ||
867 | /* allocate 3215 request structures */ | 867 | /* allocate 3215 request structures */ |
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index d52fb57a6b19..fc7a213e591f 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c | |||
@@ -591,8 +591,8 @@ con3270_init(void) | |||
591 | 591 | ||
592 | /* Set the console mode for VM */ | 592 | /* Set the console mode for VM */ |
593 | if (MACHINE_IS_VM) { | 593 | if (MACHINE_IS_VM) { |
594 | cpcmd("TERM CONMODE 3270", 0, 0); | 594 | cpcmd("TERM CONMODE 3270", NULL, 0, NULL); |
595 | cpcmd("TERM AUTOCR OFF", 0, 0); | 595 | cpcmd("TERM AUTOCR OFF", NULL, 0, NULL); |
596 | } | 596 | } |
597 | 597 | ||
598 | cdev = ccw_device_probe_console(); | 598 | cdev = ccw_device_probe_console(); |
diff --git a/drivers/s390/char/vmcp.c b/drivers/s390/char/vmcp.c new file mode 100644 index 000000000000..dfbbf235ca2b --- /dev/null +++ b/drivers/s390/char/vmcp.c | |||
@@ -0,0 +1,219 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004,2005 IBM Corporation | ||
3 | * Interface implementation for communication with the v/VM control program | ||
4 | * Author(s): Christian Borntraeger <cborntra@de.ibm.com> | ||
5 | * | ||
6 | * | ||
7 | * z/VMs CP offers the possibility to issue commands via the diagnose code 8 | ||
8 | * this driver implements a character device that issues these commands and | ||
9 | * returns the answer of CP. | ||
10 | |||
11 | * The idea of this driver is based on cpint from Neale Ferguson and #CP in CMS | ||
12 | */ | ||
13 | |||
14 | #include <linux/fs.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/miscdevice.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <asm/cpcmd.h> | ||
20 | #include <asm/debug.h> | ||
21 | #include <asm/uaccess.h> | ||
22 | #include "vmcp.h" | ||
23 | |||
24 | MODULE_LICENSE("GPL"); | ||
25 | MODULE_AUTHOR("Christian Borntraeger <cborntra@de.ibm.com>"); | ||
26 | MODULE_DESCRIPTION("z/VM CP interface"); | ||
27 | |||
28 | static debug_info_t *vmcp_debug; | ||
29 | |||
30 | static int vmcp_open(struct inode *inode, struct file *file) | ||
31 | { | ||
32 | struct vmcp_session *session; | ||
33 | |||
34 | if (!capable(CAP_SYS_ADMIN)) | ||
35 | return -EPERM; | ||
36 | |||
37 | session = kmalloc(sizeof(*session), GFP_KERNEL); | ||
38 | if (!session) | ||
39 | return -ENOMEM; | ||
40 | session->bufsize = PAGE_SIZE; | ||
41 | session->response = NULL; | ||
42 | session->resp_size = 0; | ||
43 | init_MUTEX(&session->mutex); | ||
44 | file->private_data = session; | ||
45 | return nonseekable_open(inode, file); | ||
46 | } | ||
47 | |||
48 | static int vmcp_release(struct inode *inode, struct file *file) | ||
49 | { | ||
50 | struct vmcp_session *session; | ||
51 | |||
52 | session = (struct vmcp_session *)file->private_data; | ||
53 | file->private_data = NULL; | ||
54 | free_pages((unsigned long)session->response, get_order(session->bufsize)); | ||
55 | kfree(session); | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | static ssize_t | ||
60 | vmcp_read(struct file *file, char __user * buff, size_t count, loff_t * ppos) | ||
61 | { | ||
62 | size_t tocopy; | ||
63 | struct vmcp_session *session; | ||
64 | |||
65 | session = (struct vmcp_session *)file->private_data; | ||
66 | if (down_interruptible(&session->mutex)) | ||
67 | return -ERESTARTSYS; | ||
68 | if (!session->response) { | ||
69 | up(&session->mutex); | ||
70 | return 0; | ||
71 | } | ||
72 | if (*ppos > session->resp_size) { | ||
73 | up(&session->mutex); | ||
74 | return 0; | ||
75 | } | ||
76 | tocopy = min(session->resp_size - (size_t) (*ppos), count); | ||
77 | tocopy = min(tocopy,session->bufsize - (size_t) (*ppos)); | ||
78 | |||
79 | if (copy_to_user(buff, session->response + (*ppos), tocopy)) { | ||
80 | up(&session->mutex); | ||
81 | return -EFAULT; | ||
82 | } | ||
83 | up(&session->mutex); | ||
84 | *ppos += tocopy; | ||
85 | return tocopy; | ||
86 | } | ||
87 | |||
88 | static ssize_t | ||
89 | vmcp_write(struct file *file, const char __user * buff, size_t count, | ||
90 | loff_t * ppos) | ||
91 | { | ||
92 | char *cmd; | ||
93 | struct vmcp_session *session; | ||
94 | |||
95 | if (count > 240) | ||
96 | return -EINVAL; | ||
97 | cmd = kmalloc(count + 1, GFP_KERNEL); | ||
98 | if (!cmd) | ||
99 | return -ENOMEM; | ||
100 | if (copy_from_user(cmd, buff, count)) { | ||
101 | kfree(cmd); | ||
102 | return -EFAULT; | ||
103 | } | ||
104 | cmd[count] = '\0'; | ||
105 | session = (struct vmcp_session *)file->private_data; | ||
106 | if (down_interruptible(&session->mutex)) | ||
107 | return -ERESTARTSYS; | ||
108 | if (!session->response) | ||
109 | session->response = (char *)__get_free_pages(GFP_KERNEL | ||
110 | | __GFP_REPEAT | GFP_DMA, | ||
111 | get_order(session->bufsize)); | ||
112 | if (!session->response) { | ||
113 | up(&session->mutex); | ||
114 | kfree(cmd); | ||
115 | return -ENOMEM; | ||
116 | } | ||
117 | debug_text_event(vmcp_debug, 1, cmd); | ||
118 | session->resp_size = cpcmd(cmd, session->response, | ||
119 | session->bufsize, | ||
120 | &session->resp_code); | ||
121 | up(&session->mutex); | ||
122 | kfree(cmd); | ||
123 | *ppos = 0; /* reset the file pointer after a command */ | ||
124 | return count; | ||
125 | } | ||
126 | |||
127 | |||
128 | /* | ||
129 | * These ioctls are available, as the semantics of the diagnose 8 call | ||
130 | * does not fit very well into a Linux call. Diagnose X'08' is described in | ||
131 | * CP Programming Services SC24-6084-00 | ||
132 | * | ||
133 | * VMCP_GETCODE: gives the CP return code back to user space | ||
134 | * VMCP_SETBUF: sets the response buffer for the next write call. diagnose 8 | ||
135 | * expects adjacent pages in real storage and to make matters worse, we | ||
136 | * dont know the size of the response. Therefore we default to PAGESIZE and | ||
137 | * let userspace to change the response size, if userspace expects a bigger | ||
138 | * response | ||
139 | */ | ||
140 | static long vmcp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | ||
141 | { | ||
142 | struct vmcp_session *session; | ||
143 | int temp; | ||
144 | |||
145 | session = (struct vmcp_session *)file->private_data; | ||
146 | if (down_interruptible(&session->mutex)) | ||
147 | return -ERESTARTSYS; | ||
148 | switch (cmd) { | ||
149 | case VMCP_GETCODE: | ||
150 | temp = session->resp_code; | ||
151 | up(&session->mutex); | ||
152 | return put_user(temp, (int __user *)arg); | ||
153 | case VMCP_SETBUF: | ||
154 | free_pages((unsigned long)session->response, | ||
155 | get_order(session->bufsize)); | ||
156 | session->response=NULL; | ||
157 | temp = get_user(session->bufsize, (int __user *)arg); | ||
158 | if (get_order(session->bufsize) > 8) { | ||
159 | session->bufsize = PAGE_SIZE; | ||
160 | temp = -EINVAL; | ||
161 | } | ||
162 | up(&session->mutex); | ||
163 | return temp; | ||
164 | case VMCP_GETSIZE: | ||
165 | temp = session->resp_size; | ||
166 | up(&session->mutex); | ||
167 | return put_user(temp, (int __user *)arg); | ||
168 | default: | ||
169 | up(&session->mutex); | ||
170 | return -ENOIOCTLCMD; | ||
171 | } | ||
172 | } | ||
173 | |||
174 | static struct file_operations vmcp_fops = { | ||
175 | .owner = THIS_MODULE, | ||
176 | .open = &vmcp_open, | ||
177 | .release = &vmcp_release, | ||
178 | .read = &vmcp_read, | ||
179 | .llseek = &no_llseek, | ||
180 | .write = &vmcp_write, | ||
181 | .unlocked_ioctl = &vmcp_ioctl, | ||
182 | .compat_ioctl = &vmcp_ioctl | ||
183 | }; | ||
184 | |||
185 | static struct miscdevice vmcp_dev = { | ||
186 | .name = "vmcp", | ||
187 | .minor = MISC_DYNAMIC_MINOR, | ||
188 | .fops = &vmcp_fops, | ||
189 | }; | ||
190 | |||
191 | static int __init vmcp_init(void) | ||
192 | { | ||
193 | int ret; | ||
194 | |||
195 | if (!MACHINE_IS_VM) { | ||
196 | printk(KERN_WARNING | ||
197 | "z/VM CP interface is only available under z/VM\n"); | ||
198 | return -ENODEV; | ||
199 | } | ||
200 | ret = misc_register(&vmcp_dev); | ||
201 | if (!ret) | ||
202 | printk(KERN_INFO "z/VM CP interface loaded\n"); | ||
203 | else | ||
204 | printk(KERN_WARNING | ||
205 | "z/VM CP interface not loaded. Could not register misc device.\n"); | ||
206 | vmcp_debug = debug_register("vmcp", 0, 1, 240); | ||
207 | debug_register_view(vmcp_debug, &debug_hex_ascii_view); | ||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | static void __exit vmcp_exit(void) | ||
212 | { | ||
213 | WARN_ON(misc_deregister(&vmcp_dev) != 0); | ||
214 | debug_unregister(vmcp_debug); | ||
215 | printk(KERN_INFO "z/VM CP interface unloaded.\n"); | ||
216 | } | ||
217 | |||
218 | module_init(vmcp_init); | ||
219 | module_exit(vmcp_exit); | ||
diff --git a/drivers/s390/char/vmcp.h b/drivers/s390/char/vmcp.h new file mode 100644 index 000000000000..87389e730465 --- /dev/null +++ b/drivers/s390/char/vmcp.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004, 2005 IBM Corporation | ||
3 | * Interface implementation for communication with the v/VM control program | ||
4 | * Version 1.0 | ||
5 | * Author(s): Christian Borntraeger <cborntra@de.ibm.com> | ||
6 | * | ||
7 | * | ||
8 | * z/VMs CP offers the possibility to issue commands via the diagnose code 8 | ||
9 | * this driver implements a character device that issues these commands and | ||
10 | * returns the answer of CP. | ||
11 | * | ||
12 | * The idea of this driver is based on cpint from Neale Ferguson | ||
13 | */ | ||
14 | |||
15 | #include <asm/semaphore.h> | ||
16 | #include <linux/ioctl.h> | ||
17 | |||
18 | #define VMCP_GETCODE _IOR(0x10, 1, int) | ||
19 | #define VMCP_SETBUF _IOW(0x10, 2, int) | ||
20 | #define VMCP_GETSIZE _IOR(0x10, 3, int) | ||
21 | |||
22 | struct vmcp_session { | ||
23 | unsigned int bufsize; | ||
24 | char *response; | ||
25 | int resp_size; | ||
26 | int resp_code; | ||
27 | /* As we use copy_from/to_user, which might * | ||
28 | * sleep and cannot use a spinlock */ | ||
29 | struct semaphore mutex; | ||
30 | }; | ||
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index f7717327d15e..491f00c032e8 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c | |||
@@ -236,7 +236,7 @@ vmlogrdr_get_recording_class_AB(void) { | |||
236 | int len,i; | 236 | int len,i; |
237 | 237 | ||
238 | printk (KERN_DEBUG "vmlogrdr: query command: %s\n", cp_command); | 238 | printk (KERN_DEBUG "vmlogrdr: query command: %s\n", cp_command); |
239 | cpcmd(cp_command, cp_response, sizeof(cp_response)); | 239 | cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); |
240 | printk (KERN_DEBUG "vmlogrdr: response: %s", cp_response); | 240 | printk (KERN_DEBUG "vmlogrdr: response: %s", cp_response); |
241 | len = strnlen(cp_response,sizeof(cp_response)); | 241 | len = strnlen(cp_response,sizeof(cp_response)); |
242 | // now the parsing | 242 | // now the parsing |
@@ -288,7 +288,7 @@ vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, int action, int purge) { | |||
288 | 288 | ||
289 | printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", | 289 | printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", |
290 | cp_command); | 290 | cp_command); |
291 | cpcmd(cp_command, cp_response, sizeof(cp_response)); | 291 | cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); |
292 | printk (KERN_DEBUG "vmlogrdr: recording response: %s", | 292 | printk (KERN_DEBUG "vmlogrdr: recording response: %s", |
293 | cp_response); | 293 | cp_response); |
294 | } | 294 | } |
@@ -301,7 +301,7 @@ vmlogrdr_recording(struct vmlogrdr_priv_t * logptr, int action, int purge) { | |||
301 | qid_string); | 301 | qid_string); |
302 | 302 | ||
303 | printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command); | 303 | printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command); |
304 | cpcmd(cp_command, cp_response, sizeof(cp_response)); | 304 | cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); |
305 | printk (KERN_DEBUG "vmlogrdr: recording response: %s", | 305 | printk (KERN_DEBUG "vmlogrdr: recording response: %s", |
306 | cp_response); | 306 | cp_response); |
307 | /* The recording command will usually answer with 'Command complete' | 307 | /* The recording command will usually answer with 'Command complete' |
@@ -607,7 +607,7 @@ vmlogrdr_purge_store(struct device * dev, struct device_attribute *attr, const c | |||
607 | priv->recording_name); | 607 | priv->recording_name); |
608 | 608 | ||
609 | printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command); | 609 | printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", cp_command); |
610 | cpcmd(cp_command, cp_response, sizeof(cp_response)); | 610 | cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); |
611 | printk (KERN_DEBUG "vmlogrdr: recording response: %s", | 611 | printk (KERN_DEBUG "vmlogrdr: recording response: %s", |
612 | cp_response); | 612 | cp_response); |
613 | 613 | ||
@@ -682,7 +682,7 @@ vmlogrdr_recording_status_show(struct device_driver *driver, char *buf) { | |||
682 | char cp_command[] = "QUERY RECORDING "; | 682 | char cp_command[] = "QUERY RECORDING "; |
683 | int len; | 683 | int len; |
684 | 684 | ||
685 | cpcmd(cp_command, buf, 4096); | 685 | cpcmd(cp_command, buf, 4096, NULL); |
686 | len = strlen(buf); | 686 | len = strlen(buf); |
687 | return len; | 687 | return len; |
688 | } | 688 | } |