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