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 | |
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>
-rw-r--r-- | arch/s390/defconfig | 1 | ||||
-rw-r--r-- | arch/s390/kernel/cpcmd.c | 109 | ||||
-rw-r--r-- | arch/s390/kernel/setup.c | 6 | ||||
-rw-r--r-- | arch/s390/kernel/smp.c | 6 | ||||
-rw-r--r-- | arch/s390/kernel/traps.c | 2 | ||||
-rw-r--r-- | arch/s390/mm/extmem.c | 4 | ||||
-rw-r--r-- | drivers/s390/Kconfig | 7 | ||||
-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 | ||||
-rw-r--r-- | drivers/s390/net/smsgiucv.c | 4 | ||||
-rw-r--r-- | include/asm-s390/cpcmd.h | 18 |
15 files changed, 359 insertions, 66 deletions
diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 07fd0414a4bf..89850b2c27ea 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig | |||
@@ -245,6 +245,7 @@ CONFIG_S390_TAPE_BLOCK=y | |||
245 | # | 245 | # |
246 | CONFIG_S390_TAPE_34XX=m | 246 | CONFIG_S390_TAPE_34XX=m |
247 | # CONFIG_VMLOGRDR is not set | 247 | # CONFIG_VMLOGRDR is not set |
248 | # CONFIG_VMCP is not set | ||
248 | # CONFIG_MONREADER is not set | 249 | # CONFIG_MONREADER is not set |
249 | # CONFIG_DCSS_SHM is not set | 250 | # CONFIG_DCSS_SHM is not set |
250 | 251 | ||
diff --git a/arch/s390/kernel/cpcmd.c b/arch/s390/kernel/cpcmd.c index 44df8dc07c59..20062145e84e 100644 --- a/arch/s390/kernel/cpcmd.c +++ b/arch/s390/kernel/cpcmd.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * arch/s390/kernel/cpcmd.c | 2 | * arch/s390/kernel/cpcmd.c |
3 | * | 3 | * |
4 | * S390 version | 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 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), | 6 | * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), |
7 | * Christian Borntraeger (cborntra@de.ibm.com), | 7 | * Christian Borntraeger (cborntra@de.ibm.com), |
8 | */ | 8 | */ |
@@ -18,93 +18,114 @@ | |||
18 | #include <asm/system.h> | 18 | #include <asm/system.h> |
19 | 19 | ||
20 | static DEFINE_SPINLOCK(cpcmd_lock); | 20 | static DEFINE_SPINLOCK(cpcmd_lock); |
21 | static char cpcmd_buf[240]; | 21 | static char cpcmd_buf[241]; |
22 | 22 | ||
23 | /* | 23 | /* |
24 | * the caller of __cpcmd has to ensure that the response buffer is below 2 GB | 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 | const int mask = 0x40000000L; | 28 | const int mask = 0x40000000L; |
29 | unsigned long flags; | 29 | unsigned long flags; |
30 | int return_code; | ||
31 | int return_len; | ||
30 | int cmdlen; | 32 | int cmdlen; |
31 | 33 | ||
32 | spin_lock_irqsave(&cpcmd_lock, flags); | 34 | spin_lock_irqsave(&cpcmd_lock, flags); |
33 | cmdlen = strlen(cmd); | 35 | cmdlen = strlen(cmd); |
34 | BUG_ON(cmdlen > 240); | 36 | BUG_ON(cmdlen > 240); |
35 | strcpy(cpcmd_buf, cmd); | 37 | memcpy(cpcmd_buf, cmd, cmdlen); |
36 | ASCEBC(cpcmd_buf, cmdlen); | 38 | ASCEBC(cpcmd_buf, cmdlen); |
37 | 39 | ||
38 | if (response != NULL && rlen > 0) { | 40 | if (response != NULL && rlen > 0) { |
39 | memset(response, 0, rlen); | 41 | memset(response, 0, rlen); |
40 | #ifndef CONFIG_ARCH_S390X | 42 | #ifndef CONFIG_ARCH_S390X |
41 | asm volatile ("LRA 2,0(%0)\n\t" | 43 | asm volatile ( "lra 2,0(%2)\n" |
42 | "LR 4,%1\n\t" | 44 | "lr 4,%3\n" |
43 | "O 4,%4\n\t" | 45 | "o 4,%6\n" |
44 | "LRA 3,0(%2)\n\t" | 46 | "lra 3,0(%4)\n" |
45 | "LR 5,%3\n\t" | 47 | "lr 5,%5\n" |
46 | ".long 0x83240008 # Diagnose X'08'\n\t" | 48 | "diag 2,4,0x8\n" |
47 | : /* no output */ | 49 | "brc 8, .Litfits\n" |
48 | : "a" (cpcmd_buf), "d" (cmdlen), | 50 | "ar 5, %5\n" |
49 | "a" (response), "d" (rlen), "m" (mask) | 51 | ".Litfits: \n" |
50 | : "cc", "2", "3", "4", "5" ); | 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 | #else /* CONFIG_ARCH_S390X */ | 58 | #else /* CONFIG_ARCH_S390X */ |
52 | asm volatile (" lrag 2,0(%0)\n" | 59 | asm volatile ( "lrag 2,0(%2)\n" |
53 | " lgr 4,%1\n" | 60 | "lgr 4,%3\n" |
54 | " o 4,%4\n" | 61 | "o 4,%6\n" |
55 | " lrag 3,0(%2)\n" | 62 | "lrag 3,0(%4)\n" |
56 | " lgr 5,%3\n" | 63 | "lgr 5,%5\n" |
57 | " sam31\n" | 64 | "sam31\n" |
58 | " .long 0x83240008 # Diagnose X'08'\n" | 65 | "diag 2,4,0x8\n" |
59 | " sam64" | 66 | "sam64\n" |
60 | : /* no output */ | 67 | "brc 8, .Litfits\n" |
61 | : "a" (cpcmd_buf), "d" (cmdlen), | 68 | "agr 5, %5\n" |
62 | "a" (response), "d" (rlen), "m" (mask) | 69 | ".Litfits: \n" |
63 | : "cc", "2", "3", "4", "5" ); | 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 | #endif /* CONFIG_ARCH_S390X */ | 76 | #endif /* CONFIG_ARCH_S390X */ |
65 | EBCASC(response, rlen); | 77 | EBCASC(response, rlen); |
66 | } else { | 78 | } else { |
79 | return_len = 0; | ||
67 | #ifndef CONFIG_ARCH_S390X | 80 | #ifndef CONFIG_ARCH_S390X |
68 | asm volatile ("LRA 2,0(%0)\n\t" | 81 | asm volatile ( "lra 2,0(%1)\n" |
69 | "LR 3,%1\n\t" | 82 | "lr 3,%2\n" |
70 | ".long 0x83230008 # Diagnose X'08'\n\t" | 83 | "diag 2,3,0x8\n" |
71 | : /* no output */ | 84 | "lr %0,3\n" |
72 | : "a" (cpcmd_buf), "d" (cmdlen) | 85 | : "=d" (return_code) |
73 | : "2", "3" ); | 86 | : "a" (cpcmd_buf), "d" (cmdlen) |
87 | : "2", "3" ); | ||
74 | #else /* CONFIG_ARCH_S390X */ | 88 | #else /* CONFIG_ARCH_S390X */ |
75 | asm volatile (" lrag 2,0(%0)\n" | 89 | asm volatile ( "lrag 2,0(%1)\n" |
76 | " lgr 3,%1\n" | 90 | "lgr 3,%2\n" |
77 | " sam31\n" | 91 | "sam31\n" |
78 | " .long 0x83230008 # Diagnose X'08'\n" | 92 | "diag 2,3,0x8\n" |
79 | " sam64" | 93 | "sam64\n" |
80 | : /* no output */ | 94 | "lgr %0,3\n" |
81 | : "a" (cpcmd_buf), "d" (cmdlen) | 95 | : "=d" (return_code) |
82 | : "2", "3" ); | 96 | : "a" (cpcmd_buf), "d" (cmdlen) |
97 | : "2", "3" ); | ||
83 | #endif /* CONFIG_ARCH_S390X */ | 98 | #endif /* CONFIG_ARCH_S390X */ |
84 | } | 99 | } |
85 | spin_unlock_irqrestore(&cpcmd_lock, flags); | 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 | EXPORT_SYMBOL(__cpcmd); | 106 | EXPORT_SYMBOL(__cpcmd); |
89 | 107 | ||
90 | #ifdef CONFIG_ARCH_S390X | 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 | char *lowbuf; | 111 | char *lowbuf; |
112 | int len; | ||
113 | |||
94 | if ((rlen == 0) || (response == NULL) | 114 | if ((rlen == 0) || (response == NULL) |
95 | || !((unsigned long)response >> 31)) | 115 | || !((unsigned long)response >> 31)) |
96 | __cpcmd(cmd, response, rlen); | 116 | len = __cpcmd(cmd, response, rlen, response_code); |
97 | else { | 117 | else { |
98 | lowbuf = kmalloc(rlen, GFP_KERNEL | GFP_DMA); | 118 | lowbuf = kmalloc(rlen, GFP_KERNEL | GFP_DMA); |
99 | if (!lowbuf) { | 119 | if (!lowbuf) { |
100 | printk(KERN_WARNING | 120 | printk(KERN_WARNING |
101 | "cpcmd: could not allocate response buffer\n"); | 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 | memcpy(response, lowbuf, rlen); | 125 | memcpy(response, lowbuf, rlen); |
106 | kfree(lowbuf); | 126 | kfree(lowbuf); |
107 | } | 127 | } |
128 | return len; | ||
108 | } | 129 | } |
109 | 130 | ||
110 | EXPORT_SYMBOL(cpcmd); | 131 | EXPORT_SYMBOL(cpcmd); |
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index eb7be0ad7175..b6d740ac0e6e 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c | |||
@@ -198,11 +198,11 @@ static void __init conmode_default(void) | |||
198 | char *ptr; | 198 | char *ptr; |
199 | 199 | ||
200 | if (MACHINE_IS_VM) { | 200 | if (MACHINE_IS_VM) { |
201 | __cpcmd("QUERY CONSOLE", query_buffer, 1024); | 201 | __cpcmd("QUERY CONSOLE", query_buffer, 1024, NULL); |
202 | console_devno = simple_strtoul(query_buffer + 5, NULL, 16); | 202 | console_devno = simple_strtoul(query_buffer + 5, NULL, 16); |
203 | ptr = strstr(query_buffer, "SUBCHANNEL ="); | 203 | ptr = strstr(query_buffer, "SUBCHANNEL ="); |
204 | console_irq = simple_strtoul(ptr + 13, NULL, 16); | 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 | ptr = strstr(query_buffer, "CONMODE"); | 206 | ptr = strstr(query_buffer, "CONMODE"); |
207 | /* | 207 | /* |
208 | * Set the conmode to 3215 so that the device recognition | 208 | * Set the conmode to 3215 so that the device recognition |
@@ -211,7 +211,7 @@ static void __init conmode_default(void) | |||
211 | * 3215 and the 3270 driver will try to access the console | 211 | * 3215 and the 3270 driver will try to access the console |
212 | * device (3215 as console and 3270 as normal tty). | 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 | if (ptr == NULL) { | 215 | if (ptr == NULL) { |
216 | #if defined(CONFIG_SCLP_CONSOLE) | 216 | #if defined(CONFIG_SCLP_CONSOLE) |
217 | SET_CONSOLE_SCLP; | 217 | SET_CONSOLE_SCLP; |
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 50c335067cfe..642572a8e334 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c | |||
@@ -284,7 +284,7 @@ static void do_machine_restart(void * __unused) | |||
284 | * locks are always held disabled). | 284 | * locks are always held disabled). |
285 | */ | 285 | */ |
286 | if (MACHINE_IS_VM) | 286 | if (MACHINE_IS_VM) |
287 | cpcmd ("IPL", NULL, 0); | 287 | cpcmd ("IPL", NULL, 0, NULL); |
288 | else | 288 | else |
289 | reipl (0x10000 | S390_lowcore.ipl_device); | 289 | reipl (0x10000 | S390_lowcore.ipl_device); |
290 | } | 290 | } |
@@ -313,7 +313,7 @@ static void do_machine_halt(void * __unused) | |||
313 | if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) { | 313 | if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) { |
314 | smp_send_stop(); | 314 | smp_send_stop(); |
315 | if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) | 315 | if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) |
316 | cpcmd(vmhalt_cmd, NULL, 0); | 316 | cpcmd(vmhalt_cmd, NULL, 0, NULL); |
317 | signal_processor(smp_processor_id(), | 317 | signal_processor(smp_processor_id(), |
318 | sigp_stop_and_store_status); | 318 | sigp_stop_and_store_status); |
319 | } | 319 | } |
@@ -332,7 +332,7 @@ static void do_machine_power_off(void * __unused) | |||
332 | if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) { | 332 | if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) { |
333 | smp_send_stop(); | 333 | smp_send_stop(); |
334 | if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) | 334 | if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) |
335 | cpcmd(vmpoff_cmd, NULL, 0); | 335 | cpcmd(vmpoff_cmd, NULL, 0, NULL); |
336 | signal_processor(smp_processor_id(), | 336 | signal_processor(smp_processor_id(), |
337 | sigp_stop_and_store_status); | 337 | sigp_stop_and_store_status); |
338 | } | 338 | } |
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index ca34b6f34b38..bc7b7be7acbe 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c | |||
@@ -735,7 +735,7 @@ void __init trap_init(void) | |||
735 | &ext_int_pfault); | 735 | &ext_int_pfault); |
736 | #endif | 736 | #endif |
737 | #ifndef CONFIG_ARCH_S390X | 737 | #ifndef CONFIG_ARCH_S390X |
738 | cpcmd("SET PAGEX ON", NULL, 0); | 738 | cpcmd("SET PAGEX ON", NULL, 0, NULL); |
739 | #endif | 739 | #endif |
740 | } | 740 | } |
741 | } | 741 | } |
diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c index 648deed17e25..c5348108ca3c 100644 --- a/arch/s390/mm/extmem.c +++ b/arch/s390/mm/extmem.c | |||
@@ -576,8 +576,8 @@ segment_save(char *name) | |||
576 | segtype_string[seg->range[i].start & 0xff]); | 576 | segtype_string[seg->range[i].start & 0xff]); |
577 | } | 577 | } |
578 | sprintf(cmd2, "SAVESEG %s", name); | 578 | sprintf(cmd2, "SAVESEG %s", name); |
579 | cpcmd(cmd1, NULL, 0); | 579 | cpcmd(cmd1, NULL, 0, NULL); |
580 | cpcmd(cmd2, NULL, 0); | 580 | cpcmd(cmd2, NULL, 0, NULL); |
581 | spin_unlock(&dcss_lock); | 581 | spin_unlock(&dcss_lock); |
582 | } | 582 | } |
583 | 583 | ||
diff --git a/drivers/s390/Kconfig b/drivers/s390/Kconfig index 96413c2cd1ad..a86a650f3d6d 100644 --- a/drivers/s390/Kconfig +++ b/drivers/s390/Kconfig | |||
@@ -187,6 +187,13 @@ config VMLOGRDR | |||
187 | *SYMPTOM. | 187 | *SYMPTOM. |
188 | This driver depends on the IUCV support driver. | 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 | config MONREADER | 197 | config MONREADER |
191 | tristate "API for reading z/VM monitor service records" | 198 | tristate "API for reading z/VM monitor service records" |
192 | depends on IUCV | 199 | depends on IUCV |
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 | } |
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c index 1e3f7f3c662f..d6469baa7e16 100644 --- a/drivers/s390/net/smsgiucv.c +++ b/drivers/s390/net/smsgiucv.c | |||
@@ -138,7 +138,7 @@ static void __exit | |||
138 | smsg_exit(void) | 138 | smsg_exit(void) |
139 | { | 139 | { |
140 | if (smsg_handle > 0) { | 140 | if (smsg_handle > 0) { |
141 | cpcmd("SET SMSG OFF", 0, 0); | 141 | cpcmd("SET SMSG OFF", NULL, 0, NULL); |
142 | iucv_sever(smsg_pathid, 0); | 142 | iucv_sever(smsg_pathid, 0); |
143 | iucv_unregister_program(smsg_handle); | 143 | iucv_unregister_program(smsg_handle); |
144 | driver_unregister(&smsg_driver); | 144 | driver_unregister(&smsg_driver); |
@@ -177,7 +177,7 @@ smsg_init(void) | |||
177 | smsg_handle = 0; | 177 | smsg_handle = 0; |
178 | return -EIO; | 178 | return -EIO; |
179 | } | 179 | } |
180 | cpcmd("SET SMSG IUCV", 0, 0); | 180 | cpcmd("SET SMSG IUCV", NULL, 0, NULL); |
181 | return 0; | 181 | return 0; |
182 | } | 182 | } |
183 | 183 | ||
diff --git a/include/asm-s390/cpcmd.h b/include/asm-s390/cpcmd.h index 1d33c5da083e..1fcf65be7a23 100644 --- a/include/asm-s390/cpcmd.h +++ b/include/asm-s390/cpcmd.h | |||
@@ -11,14 +11,28 @@ | |||
11 | #define __CPCMD__ | 11 | #define __CPCMD__ |
12 | 12 | ||
13 | /* | 13 | /* |
14 | * the lowlevel function for cpcmd | ||
14 | * the caller of __cpcmd has to ensure that the response buffer is below 2 GB | 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 | #ifndef __s390x__ | 19 | #ifndef __s390x__ |
19 | #define cpcmd __cpcmd | 20 | #define cpcmd __cpcmd |
20 | #else | 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 | #endif /*__s390x__*/ | 36 | #endif /*__s390x__*/ |
23 | 37 | ||
24 | #endif | 38 | #endif |