blob: d47fecb42cc5f24d5e2aeda18424fb60f37ffb25 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * arch/s390/kernel/cpcmd.c
3 *
4 * S390 version
Christian Borntraeger6b979de2005-06-25 14:55:32 -07005 * Copyright (C) 1999,2005 IBM Deutschland Entwicklung GmbH, IBM Corporation
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
7 * Christian Borntraeger (cborntra@de.ibm.com),
8 */
9
10#include <linux/kernel.h>
11#include <linux/module.h>
12#include <linux/slab.h>
13#include <linux/spinlock.h>
14#include <linux/stddef.h>
15#include <linux/string.h>
16#include <asm/ebcdic.h>
17#include <asm/cpcmd.h>
18#include <asm/system.h>
19
20static DEFINE_SPINLOCK(cpcmd_lock);
Christian Borntraeger6b979de2005-06-25 14:55:32 -070021static char cpcmd_buf[241];
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
23/*
24 * the caller of __cpcmd has to ensure that the response buffer is below 2 GB
25 */
Christian Borntraeger6b979de2005-06-25 14:55:32 -070026int __cpcmd(const char *cmd, char *response, int rlen, int *response_code)
Linus Torvalds1da177e2005-04-16 15:20:36 -070027{
28 const int mask = 0x40000000L;
29 unsigned long flags;
Christian Borntraeger6b979de2005-06-25 14:55:32 -070030 int return_code;
31 int return_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -070032 int cmdlen;
33
34 spin_lock_irqsave(&cpcmd_lock, flags);
35 cmdlen = strlen(cmd);
36 BUG_ON(cmdlen > 240);
Christian Borntraeger6b979de2005-06-25 14:55:32 -070037 memcpy(cpcmd_buf, cmd, cmdlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 ASCEBC(cpcmd_buf, cmdlen);
39
40 if (response != NULL && rlen > 0) {
41 memset(response, 0, rlen);
42#ifndef CONFIG_ARCH_S390X
Christian Borntraeger6b979de2005-06-25 14:55:32 -070043 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"
Al Viro0cbdff42005-08-23 22:47:32 +010049 "brc 8, 1f\n"
Christian Borntraeger6b979de2005-06-25 14:55:32 -070050 "ar 5, %5\n"
Al Viro0cbdff42005-08-23 22:47:32 +010051 "1: \n"
Christian Borntraeger6b979de2005-06-25 14:55:32 -070052 "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" );
Linus Torvalds1da177e2005-04-16 15:20:36 -070058#else /* CONFIG_ARCH_S390X */
Christian Borntraeger6b979de2005-06-25 14:55:32 -070059 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"
Al Viro0cbdff42005-08-23 22:47:32 +010067 "brc 8, 1f\n"
Christian Borntraeger6b979de2005-06-25 14:55:32 -070068 "agr 5, %5\n"
Al Viro0cbdff42005-08-23 22:47:32 +010069 "1: \n"
Christian Borntraeger6b979de2005-06-25 14:55:32 -070070 "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" );
Linus Torvalds1da177e2005-04-16 15:20:36 -070076#endif /* CONFIG_ARCH_S390X */
77 EBCASC(response, rlen);
78 } else {
Christian Borntraeger6b979de2005-06-25 14:55:32 -070079 return_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080#ifndef CONFIG_ARCH_S390X
Christian Borntraeger6b979de2005-06-25 14:55:32 -070081 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" );
Linus Torvalds1da177e2005-04-16 15:20:36 -070088#else /* CONFIG_ARCH_S390X */
Christian Borntraeger6b979de2005-06-25 14:55:32 -070089 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" );
Linus Torvalds1da177e2005-04-16 15:20:36 -070098#endif /* CONFIG_ARCH_S390X */
99 }
100 spin_unlock_irqrestore(&cpcmd_lock, flags);
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700101 if (response_code != NULL)
102 *response_code = return_code;
103 return return_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104}
105
106EXPORT_SYMBOL(__cpcmd);
107
108#ifdef CONFIG_ARCH_S390X
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700109int cpcmd(const char *cmd, char *response, int rlen, int *response_code)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110{
111 char *lowbuf;
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700112 int len;
113
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 if ((rlen == 0) || (response == NULL)
115 || !((unsigned long)response >> 31))
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700116 len = __cpcmd(cmd, response, rlen, response_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 else {
118 lowbuf = kmalloc(rlen, GFP_KERNEL | GFP_DMA);
119 if (!lowbuf) {
120 printk(KERN_WARNING
121 "cpcmd: could not allocate response buffer\n");
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700122 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 }
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700124 len = __cpcmd(cmd, lowbuf, rlen, response_code);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 memcpy(response, lowbuf, rlen);
126 kfree(lowbuf);
127 }
Christian Borntraeger6b979de2005-06-25 14:55:32 -0700128 return len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129}
130
131EXPORT_SYMBOL(cpcmd);
132#endif /* CONFIG_ARCH_S390X */