Merge branch 'timers-2038-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[sfrench/cifs-2.6.git] / arch / s390 / kernel / diag.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Implementation of s390 diagnose codes
4  *
5  * Copyright IBM Corp. 2007
6  * Author(s): Michael Holzheu <holzheu@de.ibm.com>
7  */
8
9 #include <linux/export.h>
10 #include <linux/init.h>
11 #include <linux/cpu.h>
12 #include <linux/seq_file.h>
13 #include <linux/debugfs.h>
14 #include <asm/diag.h>
15 #include <asm/trace/diag.h>
16
17 struct diag_stat {
18         unsigned int counter[NR_DIAG_STAT];
19 };
20
21 static DEFINE_PER_CPU(struct diag_stat, diag_stat);
22
23 struct diag_desc {
24         int code;
25         char *name;
26 };
27
28 static const struct diag_desc diag_map[NR_DIAG_STAT] = {
29         [DIAG_STAT_X008] = { .code = 0x008, .name = "Console Function" },
30         [DIAG_STAT_X00C] = { .code = 0x00c, .name = "Pseudo Timer" },
31         [DIAG_STAT_X010] = { .code = 0x010, .name = "Release Pages" },
32         [DIAG_STAT_X014] = { .code = 0x014, .name = "Spool File Services" },
33         [DIAG_STAT_X044] = { .code = 0x044, .name = "Voluntary Timeslice End" },
34         [DIAG_STAT_X064] = { .code = 0x064, .name = "NSS Manipulation" },
35         [DIAG_STAT_X09C] = { .code = 0x09c, .name = "Relinquish Timeslice" },
36         [DIAG_STAT_X0DC] = { .code = 0x0dc, .name = "Appldata Control" },
37         [DIAG_STAT_X204] = { .code = 0x204, .name = "Logical-CPU Utilization" },
38         [DIAG_STAT_X210] = { .code = 0x210, .name = "Device Information" },
39         [DIAG_STAT_X224] = { .code = 0x224, .name = "EBCDIC-Name Table" },
40         [DIAG_STAT_X250] = { .code = 0x250, .name = "Block I/O" },
41         [DIAG_STAT_X258] = { .code = 0x258, .name = "Page-Reference Services" },
42         [DIAG_STAT_X26C] = { .code = 0x26c, .name = "Certain System Information" },
43         [DIAG_STAT_X288] = { .code = 0x288, .name = "Time Bomb" },
44         [DIAG_STAT_X2C4] = { .code = 0x2c4, .name = "FTP Services" },
45         [DIAG_STAT_X2FC] = { .code = 0x2fc, .name = "Guest Performance Data" },
46         [DIAG_STAT_X304] = { .code = 0x304, .name = "Partition-Resource Service" },
47         [DIAG_STAT_X308] = { .code = 0x308, .name = "List-Directed IPL" },
48         [DIAG_STAT_X318] = { .code = 0x318, .name = "CP Name and Version Codes" },
49         [DIAG_STAT_X500] = { .code = 0x500, .name = "Virtio Service" },
50 };
51
52 static int show_diag_stat(struct seq_file *m, void *v)
53 {
54         struct diag_stat *stat;
55         unsigned long n = (unsigned long) v - 1;
56         int cpu, prec, tmp;
57
58         get_online_cpus();
59         if (n == 0) {
60                 seq_puts(m, "         ");
61
62                 for_each_online_cpu(cpu) {
63                         prec = 10;
64                         for (tmp = 10; cpu >= tmp; tmp *= 10)
65                                 prec--;
66                         seq_printf(m, "%*s%d", prec, "CPU", cpu);
67                 }
68                 seq_putc(m, '\n');
69         } else if (n <= NR_DIAG_STAT) {
70                 seq_printf(m, "diag %03x:", diag_map[n-1].code);
71                 for_each_online_cpu(cpu) {
72                         stat = &per_cpu(diag_stat, cpu);
73                         seq_printf(m, " %10u", stat->counter[n-1]);
74                 }
75                 seq_printf(m, "    %s\n", diag_map[n-1].name);
76         }
77         put_online_cpus();
78         return 0;
79 }
80
81 static void *show_diag_stat_start(struct seq_file *m, loff_t *pos)
82 {
83         return *pos <= nr_cpu_ids ? (void *)((unsigned long) *pos + 1) : NULL;
84 }
85
86 static void *show_diag_stat_next(struct seq_file *m, void *v, loff_t *pos)
87 {
88         ++*pos;
89         return show_diag_stat_start(m, pos);
90 }
91
92 static void show_diag_stat_stop(struct seq_file *m, void *v)
93 {
94 }
95
96 static const struct seq_operations show_diag_stat_sops = {
97         .start  = show_diag_stat_start,
98         .next   = show_diag_stat_next,
99         .stop   = show_diag_stat_stop,
100         .show   = show_diag_stat,
101 };
102
103 static int show_diag_stat_open(struct inode *inode, struct file *file)
104 {
105         return seq_open(file, &show_diag_stat_sops);
106 }
107
108 static const struct file_operations show_diag_stat_fops = {
109         .open           = show_diag_stat_open,
110         .read           = seq_read,
111         .llseek         = seq_lseek,
112         .release        = seq_release,
113 };
114
115
116 static int __init show_diag_stat_init(void)
117 {
118         debugfs_create_file("diag_stat", 0400, NULL, NULL,
119                             &show_diag_stat_fops);
120         return 0;
121 }
122
123 device_initcall(show_diag_stat_init);
124
125 void diag_stat_inc(enum diag_stat_enum nr)
126 {
127         this_cpu_inc(diag_stat.counter[nr]);
128         trace_s390_diagnose(diag_map[nr].code);
129 }
130 EXPORT_SYMBOL(diag_stat_inc);
131
132 void diag_stat_inc_norecursion(enum diag_stat_enum nr)
133 {
134         this_cpu_inc(diag_stat.counter[nr]);
135         trace_s390_diagnose_norecursion(diag_map[nr].code);
136 }
137 EXPORT_SYMBOL(diag_stat_inc_norecursion);
138
139 /*
140  * Diagnose 14: Input spool file manipulation
141  */
142 static inline int __diag14(unsigned long rx, unsigned long ry1,
143                            unsigned long subcode)
144 {
145         register unsigned long _ry1 asm("2") = ry1;
146         register unsigned long _ry2 asm("3") = subcode;
147         int rc = 0;
148
149         asm volatile(
150                 "   sam31\n"
151                 "   diag    %2,2,0x14\n"
152                 "   sam64\n"
153                 "   ipm     %0\n"
154                 "   srl     %0,28\n"
155                 : "=d" (rc), "+d" (_ry2)
156                 : "d" (rx), "d" (_ry1)
157                 : "cc");
158
159         return rc;
160 }
161
162 int diag14(unsigned long rx, unsigned long ry1, unsigned long subcode)
163 {
164         diag_stat_inc(DIAG_STAT_X014);
165         return __diag14(rx, ry1, subcode);
166 }
167 EXPORT_SYMBOL(diag14);
168
169 static inline int __diag204(unsigned long *subcode, unsigned long size, void *addr)
170 {
171         register unsigned long _subcode asm("0") = *subcode;
172         register unsigned long _size asm("1") = size;
173
174         asm volatile(
175                 "       diag    %2,%0,0x204\n"
176                 "0:     nopr    %%r7\n"
177                 EX_TABLE(0b,0b)
178                 : "+d" (_subcode), "+d" (_size) : "d" (addr) : "memory");
179         *subcode = _subcode;
180         return _size;
181 }
182
183 int diag204(unsigned long subcode, unsigned long size, void *addr)
184 {
185         diag_stat_inc(DIAG_STAT_X204);
186         size = __diag204(&subcode, size, addr);
187         if (subcode)
188                 return -1;
189         return size;
190 }
191 EXPORT_SYMBOL(diag204);
192
193 /*
194  * Diagnose 210: Get information about a virtual device
195  */
196 int diag210(struct diag210 *addr)
197 {
198         /*
199          * diag 210 needs its data below the 2GB border, so we
200          * use a static data area to be sure
201          */
202         static struct diag210 diag210_tmp;
203         static DEFINE_SPINLOCK(diag210_lock);
204         unsigned long flags;
205         int ccode;
206
207         spin_lock_irqsave(&diag210_lock, flags);
208         diag210_tmp = *addr;
209
210         diag_stat_inc(DIAG_STAT_X210);
211         asm volatile(
212                 "       lhi     %0,-1\n"
213                 "       sam31\n"
214                 "       diag    %1,0,0x210\n"
215                 "0:     ipm     %0\n"
216                 "       srl     %0,28\n"
217                 "1:     sam64\n"
218                 EX_TABLE(0b, 1b)
219                 : "=&d" (ccode) : "a" (&diag210_tmp) : "cc", "memory");
220
221         *addr = diag210_tmp;
222         spin_unlock_irqrestore(&diag210_lock, flags);
223
224         return ccode;
225 }
226 EXPORT_SYMBOL(diag210);
227
228 int diag224(void *ptr)
229 {
230         int rc = -EOPNOTSUPP;
231
232         diag_stat_inc(DIAG_STAT_X224);
233         asm volatile(
234                 "       diag    %1,%2,0x224\n"
235                 "0:     lhi     %0,0x0\n"
236                 "1:\n"
237                 EX_TABLE(0b,1b)
238                 : "+d" (rc) :"d" (0), "d" (ptr) : "memory");
239         return rc;
240 }
241 EXPORT_SYMBOL(diag224);
242
243 /*
244  * Diagnose 26C: Access Certain System Information
245  */
246 static inline int __diag26c(void *req, void *resp, enum diag26c_sc subcode)
247 {
248         register unsigned long _req asm("2") = (addr_t) req;
249         register unsigned long _resp asm("3") = (addr_t) resp;
250         register unsigned long _subcode asm("4") = subcode;
251         register unsigned long _rc asm("5") = -EOPNOTSUPP;
252
253         asm volatile(
254                 "       sam31\n"
255                 "       diag    %[rx],%[ry],0x26c\n"
256                 "0:     sam64\n"
257                 EX_TABLE(0b,0b)
258                 : "+d" (_rc)
259                 : [rx] "d" (_req), "d" (_resp), [ry] "d" (_subcode)
260                 : "cc", "memory");
261         return _rc;
262 }
263
264 int diag26c(void *req, void *resp, enum diag26c_sc subcode)
265 {
266         diag_stat_inc(DIAG_STAT_X26C);
267         return __diag26c(req, resp, subcode);
268 }
269 EXPORT_SYMBOL(diag26c);