~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to arch/s390/kernel/cpcmd.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  arch/s390/kernel/cpcmd.c
 
3
 *
 
4
 *  S390 version
 
5
 *    Copyright IBM Corp. 1999,2007
 
6
 *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
 
7
 *               Christian Borntraeger (cborntra@de.ibm.com),
 
8
 */
 
9
 
 
10
#define KMSG_COMPONENT "cpcmd"
 
11
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
 
12
 
 
13
#include <linux/kernel.h>
 
14
#include <linux/module.h>
 
15
#include <linux/slab.h>
 
16
#include <linux/spinlock.h>
 
17
#include <linux/stddef.h>
 
18
#include <linux/string.h>
 
19
#include <asm/ebcdic.h>
 
20
#include <asm/cpcmd.h>
 
21
#include <asm/system.h>
 
22
#include <asm/io.h>
 
23
 
 
24
static DEFINE_SPINLOCK(cpcmd_lock);
 
25
static char cpcmd_buf[241];
 
26
 
 
27
static int diag8_noresponse(int cmdlen)
 
28
{
 
29
        register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf;
 
30
        register unsigned long reg3 asm ("3") = cmdlen;
 
31
 
 
32
        asm volatile(
 
33
#ifndef CONFIG_64BIT
 
34
                "       diag    %1,%0,0x8\n"
 
35
#else /* CONFIG_64BIT */
 
36
                "       sam31\n"
 
37
                "       diag    %1,%0,0x8\n"
 
38
                "       sam64\n"
 
39
#endif /* CONFIG_64BIT */
 
40
                : "+d" (reg3) : "d" (reg2) : "cc");
 
41
        return reg3;
 
42
}
 
43
 
 
44
static int diag8_response(int cmdlen, char *response, int *rlen)
 
45
{
 
46
        register unsigned long reg2 asm ("2") = (addr_t) cpcmd_buf;
 
47
        register unsigned long reg3 asm ("3") = (addr_t) response;
 
48
        register unsigned long reg4 asm ("4") = cmdlen | 0x40000000L;
 
49
        register unsigned long reg5 asm ("5") = *rlen;
 
50
 
 
51
        asm volatile(
 
52
#ifndef CONFIG_64BIT
 
53
                "       diag    %2,%0,0x8\n"
 
54
                "       brc     8,1f\n"
 
55
                "       ar      %1,%4\n"
 
56
#else /* CONFIG_64BIT */
 
57
                "       sam31\n"
 
58
                "       diag    %2,%0,0x8\n"
 
59
                "       sam64\n"
 
60
                "       brc     8,1f\n"
 
61
                "       agr     %1,%4\n"
 
62
#endif /* CONFIG_64BIT */
 
63
                "1:\n"
 
64
                : "+d" (reg4), "+d" (reg5)
 
65
                : "d" (reg2), "d" (reg3), "d" (*rlen) : "cc");
 
66
        *rlen = reg5;
 
67
        return reg4;
 
68
}
 
69
 
 
70
/*
 
71
 * __cpcmd has some restrictions over cpcmd
 
72
 *  - the response buffer must reside below 2GB (if any)
 
73
 *  - __cpcmd is unlocked and therefore not SMP-safe
 
74
 */
 
75
int  __cpcmd(const char *cmd, char *response, int rlen, int *response_code)
 
76
{
 
77
        int cmdlen;
 
78
        int rc;
 
79
        int response_len;
 
80
 
 
81
        cmdlen = strlen(cmd);
 
82
        BUG_ON(cmdlen > 240);
 
83
        memcpy(cpcmd_buf, cmd, cmdlen);
 
84
        ASCEBC(cpcmd_buf, cmdlen);
 
85
 
 
86
        if (response) {
 
87
                memset(response, 0, rlen);
 
88
                response_len = rlen;
 
89
                rc = diag8_response(cmdlen, response, &rlen);
 
90
                EBCASC(response, response_len);
 
91
        } else {
 
92
                rc = diag8_noresponse(cmdlen);
 
93
        }
 
94
        if (response_code)
 
95
                *response_code = rc;
 
96
        return rlen;
 
97
}
 
98
EXPORT_SYMBOL(__cpcmd);
 
99
 
 
100
int cpcmd(const char *cmd, char *response, int rlen, int *response_code)
 
101
{
 
102
        char *lowbuf;
 
103
        int len;
 
104
        unsigned long flags;
 
105
 
 
106
        if ((virt_to_phys(response) != (unsigned long) response) ||
 
107
                        (((unsigned long)response + rlen) >> 31)) {
 
108
                lowbuf = kmalloc(rlen, GFP_KERNEL | GFP_DMA);
 
109
                if (!lowbuf) {
 
110
                        pr_warning("The cpcmd kernel function failed to "
 
111
                                   "allocate a response buffer\n");
 
112
                        return -ENOMEM;
 
113
                }
 
114
                spin_lock_irqsave(&cpcmd_lock, flags);
 
115
                len = __cpcmd(cmd, lowbuf, rlen, response_code);
 
116
                spin_unlock_irqrestore(&cpcmd_lock, flags);
 
117
                memcpy(response, lowbuf, rlen);
 
118
                kfree(lowbuf);
 
119
        } else {
 
120
                spin_lock_irqsave(&cpcmd_lock, flags);
 
121
                len = __cpcmd(cmd, response, rlen, response_code);
 
122
                spin_unlock_irqrestore(&cpcmd_lock, flags);
 
123
        }
 
124
        return len;
 
125
}
 
126
EXPORT_SYMBOL(cpcmd);