Commit 6b979de395c7e1b7e59f74a870e1d1911853eccb
Committed by
Linus Torvalds
1 parent
77fa22450d
Exists in
master
and in
7 other branches
[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>
Showing 15 changed files with 359 additions and 66 deletions Side-by-side Diff
- arch/s390/defconfig
- arch/s390/kernel/cpcmd.c
- arch/s390/kernel/setup.c
- arch/s390/kernel/smp.c
- arch/s390/kernel/traps.c
- arch/s390/mm/extmem.c
- drivers/s390/Kconfig
- drivers/s390/char/Makefile
- drivers/s390/char/con3215.c
- drivers/s390/char/con3270.c
- drivers/s390/char/vmcp.c
- drivers/s390/char/vmcp.h
- drivers/s390/char/vmlogrdr.c
- drivers/s390/net/smsgiucv.c
- include/asm-s390/cpcmd.h
arch/s390/defconfig
arch/s390/kernel/cpcmd.c
... | ... | @@ -2,7 +2,7 @@ |
2 | 2 | * arch/s390/kernel/cpcmd.c |
3 | 3 | * |
4 | 4 | * S390 version |
5 | - * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation | |
5 | + * Copyright (C) 1999,2005 IBM Deutschland Entwicklung GmbH, IBM Corporation | |
6 | 6 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), |
7 | 7 | * Christian Borntraeger (cborntra@de.ibm.com), |
8 | 8 | */ |
9 | 9 | |
10 | 10 | |
11 | 11 | |
12 | 12 | |
13 | 13 | |
14 | 14 | |
15 | 15 | |
16 | 16 | |
17 | 17 | |
18 | 18 | |
19 | 19 | |
20 | 20 | |
21 | 21 | |
22 | 22 | |
23 | 23 | |
... | ... | @@ -18,93 +18,114 @@ |
18 | 18 | #include <asm/system.h> |
19 | 19 | |
20 | 20 | static DEFINE_SPINLOCK(cpcmd_lock); |
21 | -static char cpcmd_buf[240]; | |
21 | +static char cpcmd_buf[241]; | |
22 | 22 | |
23 | 23 | /* |
24 | 24 | * the caller of __cpcmd has to ensure that the response buffer is below 2 GB |
25 | 25 | */ |
26 | -void __cpcmd(char *cmd, char *response, int rlen) | |
26 | +int __cpcmd(const char *cmd, char *response, int rlen, int *response_code) | |
27 | 27 | { |
28 | 28 | const int mask = 0x40000000L; |
29 | 29 | unsigned long flags; |
30 | + int return_code; | |
31 | + int return_len; | |
30 | 32 | int cmdlen; |
31 | 33 | |
32 | 34 | spin_lock_irqsave(&cpcmd_lock, flags); |
33 | 35 | cmdlen = strlen(cmd); |
34 | 36 | BUG_ON(cmdlen > 240); |
35 | - strcpy(cpcmd_buf, cmd); | |
37 | + memcpy(cpcmd_buf, cmd, cmdlen); | |
36 | 38 | ASCEBC(cpcmd_buf, cmdlen); |
37 | 39 | |
38 | 40 | if (response != NULL && rlen > 0) { |
39 | 41 | memset(response, 0, rlen); |
40 | 42 | #ifndef CONFIG_ARCH_S390X |
41 | - asm volatile ("LRA 2,0(%0)\n\t" | |
42 | - "LR 4,%1\n\t" | |
43 | - "O 4,%4\n\t" | |
44 | - "LRA 3,0(%2)\n\t" | |
45 | - "LR 5,%3\n\t" | |
46 | - ".long 0x83240008 # Diagnose X'08'\n\t" | |
47 | - : /* no output */ | |
48 | - : "a" (cpcmd_buf), "d" (cmdlen), | |
49 | - "a" (response), "d" (rlen), "m" (mask) | |
50 | - : "cc", "2", "3", "4", "5" ); | |
43 | + asm volatile ( "lra 2,0(%2)\n" | |
44 | + "lr 4,%3\n" | |
45 | + "o 4,%6\n" | |
46 | + "lra 3,0(%4)\n" | |
47 | + "lr 5,%5\n" | |
48 | + "diag 2,4,0x8\n" | |
49 | + "brc 8, .Litfits\n" | |
50 | + "ar 5, %5\n" | |
51 | + ".Litfits: \n" | |
52 | + "lr %0,4\n" | |
53 | + "lr %1,5\n" | |
54 | + : "=d" (return_code), "=d" (return_len) | |
55 | + : "a" (cpcmd_buf), "d" (cmdlen), | |
56 | + "a" (response), "d" (rlen), "m" (mask) | |
57 | + : "cc", "2", "3", "4", "5" ); | |
51 | 58 | #else /* CONFIG_ARCH_S390X */ |
52 | - asm volatile (" lrag 2,0(%0)\n" | |
53 | - " lgr 4,%1\n" | |
54 | - " o 4,%4\n" | |
55 | - " lrag 3,0(%2)\n" | |
56 | - " lgr 5,%3\n" | |
57 | - " sam31\n" | |
58 | - " .long 0x83240008 # Diagnose X'08'\n" | |
59 | - " sam64" | |
60 | - : /* no output */ | |
61 | - : "a" (cpcmd_buf), "d" (cmdlen), | |
62 | - "a" (response), "d" (rlen), "m" (mask) | |
63 | - : "cc", "2", "3", "4", "5" ); | |
59 | + asm volatile ( "lrag 2,0(%2)\n" | |
60 | + "lgr 4,%3\n" | |
61 | + "o 4,%6\n" | |
62 | + "lrag 3,0(%4)\n" | |
63 | + "lgr 5,%5\n" | |
64 | + "sam31\n" | |
65 | + "diag 2,4,0x8\n" | |
66 | + "sam64\n" | |
67 | + "brc 8, .Litfits\n" | |
68 | + "agr 5, %5\n" | |
69 | + ".Litfits: \n" | |
70 | + "lgr %0,4\n" | |
71 | + "lgr %1,5\n" | |
72 | + : "=d" (return_code), "=d" (return_len) | |
73 | + : "a" (cpcmd_buf), "d" (cmdlen), | |
74 | + "a" (response), "d" (rlen), "m" (mask) | |
75 | + : "cc", "2", "3", "4", "5" ); | |
64 | 76 | #endif /* CONFIG_ARCH_S390X */ |
65 | 77 | EBCASC(response, rlen); |
66 | 78 | } else { |
79 | + return_len = 0; | |
67 | 80 | #ifndef CONFIG_ARCH_S390X |
68 | - asm volatile ("LRA 2,0(%0)\n\t" | |
69 | - "LR 3,%1\n\t" | |
70 | - ".long 0x83230008 # Diagnose X'08'\n\t" | |
71 | - : /* no output */ | |
72 | - : "a" (cpcmd_buf), "d" (cmdlen) | |
73 | - : "2", "3" ); | |
81 | + asm volatile ( "lra 2,0(%1)\n" | |
82 | + "lr 3,%2\n" | |
83 | + "diag 2,3,0x8\n" | |
84 | + "lr %0,3\n" | |
85 | + : "=d" (return_code) | |
86 | + : "a" (cpcmd_buf), "d" (cmdlen) | |
87 | + : "2", "3" ); | |
74 | 88 | #else /* CONFIG_ARCH_S390X */ |
75 | - asm volatile (" lrag 2,0(%0)\n" | |
76 | - " lgr 3,%1\n" | |
77 | - " sam31\n" | |
78 | - " .long 0x83230008 # Diagnose X'08'\n" | |
79 | - " sam64" | |
80 | - : /* no output */ | |
81 | - : "a" (cpcmd_buf), "d" (cmdlen) | |
82 | - : "2", "3" ); | |
89 | + asm volatile ( "lrag 2,0(%1)\n" | |
90 | + "lgr 3,%2\n" | |
91 | + "sam31\n" | |
92 | + "diag 2,3,0x8\n" | |
93 | + "sam64\n" | |
94 | + "lgr %0,3\n" | |
95 | + : "=d" (return_code) | |
96 | + : "a" (cpcmd_buf), "d" (cmdlen) | |
97 | + : "2", "3" ); | |
83 | 98 | #endif /* CONFIG_ARCH_S390X */ |
84 | 99 | } |
85 | 100 | spin_unlock_irqrestore(&cpcmd_lock, flags); |
101 | + if (response_code != NULL) | |
102 | + *response_code = return_code; | |
103 | + return return_len; | |
86 | 104 | } |
87 | 105 | |
88 | 106 | EXPORT_SYMBOL(__cpcmd); |
89 | 107 | |
90 | 108 | #ifdef CONFIG_ARCH_S390X |
91 | -void cpcmd(char *cmd, char *response, int rlen) | |
109 | +int cpcmd(const char *cmd, char *response, int rlen, int *response_code) | |
92 | 110 | { |
93 | 111 | char *lowbuf; |
112 | + int len; | |
113 | + | |
94 | 114 | if ((rlen == 0) || (response == NULL) |
95 | 115 | || !((unsigned long)response >> 31)) |
96 | - __cpcmd(cmd, response, rlen); | |
116 | + len = __cpcmd(cmd, response, rlen, response_code); | |
97 | 117 | else { |
98 | 118 | lowbuf = kmalloc(rlen, GFP_KERNEL | GFP_DMA); |
99 | 119 | if (!lowbuf) { |
100 | 120 | printk(KERN_WARNING |
101 | 121 | "cpcmd: could not allocate response buffer\n"); |
102 | - return; | |
122 | + return -ENOMEM; | |
103 | 123 | } |
104 | - __cpcmd(cmd, lowbuf, rlen); | |
124 | + len = __cpcmd(cmd, lowbuf, rlen, response_code); | |
105 | 125 | memcpy(response, lowbuf, rlen); |
106 | 126 | kfree(lowbuf); |
107 | 127 | } |
128 | + return len; | |
108 | 129 | } |
109 | 130 | |
110 | 131 | EXPORT_SYMBOL(cpcmd); |
arch/s390/kernel/setup.c
... | ... | @@ -198,11 +198,11 @@ |
198 | 198 | char *ptr; |
199 | 199 | |
200 | 200 | if (MACHINE_IS_VM) { |
201 | - __cpcmd("QUERY CONSOLE", query_buffer, 1024); | |
201 | + __cpcmd("QUERY CONSOLE", query_buffer, 1024, NULL); | |
202 | 202 | console_devno = simple_strtoul(query_buffer + 5, NULL, 16); |
203 | 203 | ptr = strstr(query_buffer, "SUBCHANNEL ="); |
204 | 204 | console_irq = simple_strtoul(ptr + 13, NULL, 16); |
205 | - __cpcmd("QUERY TERM", query_buffer, 1024); | |
205 | + __cpcmd("QUERY TERM", query_buffer, 1024, NULL); | |
206 | 206 | ptr = strstr(query_buffer, "CONMODE"); |
207 | 207 | /* |
208 | 208 | * Set the conmode to 3215 so that the device recognition |
... | ... | @@ -211,7 +211,7 @@ |
211 | 211 | * 3215 and the 3270 driver will try to access the console |
212 | 212 | * device (3215 as console and 3270 as normal tty). |
213 | 213 | */ |
214 | - __cpcmd("TERM CONMODE 3215", NULL, 0); | |
214 | + __cpcmd("TERM CONMODE 3215", NULL, 0, NULL); | |
215 | 215 | if (ptr == NULL) { |
216 | 216 | #if defined(CONFIG_SCLP_CONSOLE) |
217 | 217 | SET_CONSOLE_SCLP; |
arch/s390/kernel/smp.c
... | ... | @@ -284,7 +284,7 @@ |
284 | 284 | * locks are always held disabled). |
285 | 285 | */ |
286 | 286 | if (MACHINE_IS_VM) |
287 | - cpcmd ("IPL", NULL, 0); | |
287 | + cpcmd ("IPL", NULL, 0, NULL); | |
288 | 288 | else |
289 | 289 | reipl (0x10000 | S390_lowcore.ipl_device); |
290 | 290 | } |
... | ... | @@ -313,7 +313,7 @@ |
313 | 313 | if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) { |
314 | 314 | smp_send_stop(); |
315 | 315 | if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) |
316 | - cpcmd(vmhalt_cmd, NULL, 0); | |
316 | + cpcmd(vmhalt_cmd, NULL, 0, NULL); | |
317 | 317 | signal_processor(smp_processor_id(), |
318 | 318 | sigp_stop_and_store_status); |
319 | 319 | } |
... | ... | @@ -332,7 +332,7 @@ |
332 | 332 | if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) { |
333 | 333 | smp_send_stop(); |
334 | 334 | if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) |
335 | - cpcmd(vmpoff_cmd, NULL, 0); | |
335 | + cpcmd(vmpoff_cmd, NULL, 0, NULL); | |
336 | 336 | signal_processor(smp_processor_id(), |
337 | 337 | sigp_stop_and_store_status); |
338 | 338 | } |
arch/s390/kernel/traps.c
arch/s390/mm/extmem.c
... | ... | @@ -576,8 +576,8 @@ |
576 | 576 | segtype_string[seg->range[i].start & 0xff]); |
577 | 577 | } |
578 | 578 | sprintf(cmd2, "SAVESEG %s", name); |
579 | - cpcmd(cmd1, NULL, 0); | |
580 | - cpcmd(cmd2, NULL, 0); | |
579 | + cpcmd(cmd1, NULL, 0, NULL); | |
580 | + cpcmd(cmd2, NULL, 0, NULL); | |
581 | 581 | spin_unlock(&dcss_lock); |
582 | 582 | } |
583 | 583 |
drivers/s390/Kconfig
... | ... | @@ -187,6 +187,13 @@ |
187 | 187 | *SYMPTOM. |
188 | 188 | This driver depends on the IUCV support driver. |
189 | 189 | |
190 | +config VMCP | |
191 | + tristate "Support for the z/VM CP interface (VM only)" | |
192 | + help | |
193 | + Select this option if you want to be able to interact with the control | |
194 | + program on z/VM | |
195 | + | |
196 | + | |
190 | 197 | config MONREADER |
191 | 198 | tristate "API for reading z/VM monitor service records" |
192 | 199 | depends on IUCV |
drivers/s390/char/Makefile
drivers/s390/char/con3215.c
... | ... | @@ -860,8 +860,8 @@ |
860 | 860 | |
861 | 861 | /* Set the console mode for VM */ |
862 | 862 | if (MACHINE_IS_VM) { |
863 | - cpcmd("TERM CONMODE 3215", NULL, 0); | |
864 | - cpcmd("TERM AUTOCR OFF", NULL, 0); | |
863 | + cpcmd("TERM CONMODE 3215", NULL, 0, NULL); | |
864 | + cpcmd("TERM AUTOCR OFF", NULL, 0, NULL); | |
865 | 865 | } |
866 | 866 | |
867 | 867 | /* allocate 3215 request structures */ |
drivers/s390/char/con3270.c
... | ... | @@ -591,8 +591,8 @@ |
591 | 591 | |
592 | 592 | /* Set the console mode for VM */ |
593 | 593 | if (MACHINE_IS_VM) { |
594 | - cpcmd("TERM CONMODE 3270", 0, 0); | |
595 | - cpcmd("TERM AUTOCR OFF", 0, 0); | |
594 | + cpcmd("TERM CONMODE 3270", NULL, 0, NULL); | |
595 | + cpcmd("TERM AUTOCR OFF", NULL, 0, NULL); | |
596 | 596 | } |
597 | 597 | |
598 | 598 | cdev = ccw_device_probe_console(); |
drivers/s390/char/vmcp.c
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); |
drivers/s390/char/vmcp.h
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 | +}; |
drivers/s390/char/vmlogrdr.c
... | ... | @@ -236,7 +236,7 @@ |
236 | 236 | int len,i; |
237 | 237 | |
238 | 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 | 240 | printk (KERN_DEBUG "vmlogrdr: response: %s", cp_response); |
241 | 241 | len = strnlen(cp_response,sizeof(cp_response)); |
242 | 242 | // now the parsing |
... | ... | @@ -288,7 +288,7 @@ |
288 | 288 | |
289 | 289 | printk (KERN_DEBUG "vmlogrdr: recording command: %s\n", |
290 | 290 | cp_command); |
291 | - cpcmd(cp_command, cp_response, sizeof(cp_response)); | |
291 | + cpcmd(cp_command, cp_response, sizeof(cp_response), NULL); | |
292 | 292 | printk (KERN_DEBUG "vmlogrdr: recording response: %s", |
293 | 293 | cp_response); |
294 | 294 | } |
... | ... | @@ -301,7 +301,7 @@ |
301 | 301 | qid_string); |
302 | 302 | |
303 | 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 | 305 | printk (KERN_DEBUG "vmlogrdr: recording response: %s", |
306 | 306 | cp_response); |
307 | 307 | /* The recording command will usually answer with 'Command complete' |
... | ... | @@ -607,7 +607,7 @@ |
607 | 607 | priv->recording_name); |
608 | 608 | |
609 | 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 | 611 | printk (KERN_DEBUG "vmlogrdr: recording response: %s", |
612 | 612 | cp_response); |
613 | 613 | |
... | ... | @@ -682,7 +682,7 @@ |
682 | 682 | char cp_command[] = "QUERY RECORDING "; |
683 | 683 | int len; |
684 | 684 | |
685 | - cpcmd(cp_command, buf, 4096); | |
685 | + cpcmd(cp_command, buf, 4096, NULL); | |
686 | 686 | len = strlen(buf); |
687 | 687 | return len; |
688 | 688 | } |
drivers/s390/net/smsgiucv.c
... | ... | @@ -138,7 +138,7 @@ |
138 | 138 | smsg_exit(void) |
139 | 139 | { |
140 | 140 | if (smsg_handle > 0) { |
141 | - cpcmd("SET SMSG OFF", 0, 0); | |
141 | + cpcmd("SET SMSG OFF", NULL, 0, NULL); | |
142 | 142 | iucv_sever(smsg_pathid, 0); |
143 | 143 | iucv_unregister_program(smsg_handle); |
144 | 144 | driver_unregister(&smsg_driver); |
... | ... | @@ -177,7 +177,7 @@ |
177 | 177 | smsg_handle = 0; |
178 | 178 | return -EIO; |
179 | 179 | } |
180 | - cpcmd("SET SMSG IUCV", 0, 0); | |
180 | + cpcmd("SET SMSG IUCV", NULL, 0, NULL); | |
181 | 181 | return 0; |
182 | 182 | } |
183 | 183 |
include/asm-s390/cpcmd.h
... | ... | @@ -11,14 +11,28 @@ |
11 | 11 | #define __CPCMD__ |
12 | 12 | |
13 | 13 | /* |
14 | + * the lowlevel function for cpcmd | |
14 | 15 | * the caller of __cpcmd has to ensure that the response buffer is below 2 GB |
15 | 16 | */ |
16 | -extern void __cpcmd(char *cmd, char *response, int rlen); | |
17 | +extern int __cpcmd(const char *cmd, char *response, int rlen, int *response_code); | |
17 | 18 | |
18 | 19 | #ifndef __s390x__ |
19 | 20 | #define cpcmd __cpcmd |
20 | 21 | #else |
21 | -extern void cpcmd(char *cmd, char *response, int rlen); | |
22 | +/* | |
23 | + * cpcmd is the in-kernel interface for issuing CP commands | |
24 | + * | |
25 | + * cmd: null-terminated command string, max 240 characters | |
26 | + * response: response buffer for VM's textual response | |
27 | + * rlen: size of the response buffer, cpcmd will not exceed this size | |
28 | + * but will cap the output, if its too large. Everything that | |
29 | + * did not fit into the buffer will be silently dropped | |
30 | + * response_code: return pointer for VM's error code | |
31 | + * return value: the size of the response. The caller can check if the buffer | |
32 | + * was large enough by comparing the return value and rlen | |
33 | + * NOTE: If the response buffer is not below 2 GB, cpcmd can sleep | |
34 | + */ | |
35 | +extern int cpcmd(const char *cmd, char *response, int rlen, int *response_code); | |
22 | 36 | #endif /*__s390x__*/ |
23 | 37 | |
24 | 38 | #endif |