blob: 837d83eda2f0fdc5d6b23020767ca9ac49b73885 [file] [log] [blame]
florianec82dd92011-07-07 22:09:41 +00001/* -*- mode: C; c-basic-offset: 3; -*- */
2
sewardj02b89e82011-05-17 16:19:53 +00003#include <setjmp.h>
4#include <signal.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
florianec82dd92011-07-07 22:09:41 +00008#include <ctype.h> // isspace
9#include <fcntl.h> // open
10#include <unistd.h> // lseek
11#include <sys/stat.h> // S_IRUSR
sewardj02b89e82011-05-17 16:19:53 +000012
13// This file determines s390x features a processor supports.
14//
15// We return:
florianec82dd92011-07-07 22:09:41 +000016// - 0 if the machine provides the asked-for feature and the cpu
17// model, if specified, matches the machine
18// - 1 the machine does not provide the asked-for feature or the
19// cpu model, if specified, does not match the machine
cborntra9dd35e02012-05-29 11:02:28 +000020// - 1 for an unknown cpu model in /proc/cpu_info
sewardj02b89e82011-05-17 16:19:53 +000021// - 2 if the asked-for feature isn't recognised (this will be the case for
22// any feature if run on a non-s390x machine).
23// - 3 if there was a usage error (it also prints an error message).
florianec82dd92011-07-07 22:09:41 +000024//
25// USAGE:
26//
27// s390x_features <feature> [<machine-model>]
28//
29// The machine_model is optional and it can be something like:
30//
31// z9 -- Host needs to be a z9 (and nothing else)
32// z9: -- Host needs to be a z9 or any later model
33// :z9 -- Host needs to be a model up to and including z9
34// z900:z9 -- Host needs to be at least a z900 and at most a z9.
35// Any model in between is OK, too.
sewardj02b89e82011-05-17 16:19:53 +000036
37jmp_buf env;
38
39#if defined(VGA_s390x)
40
41void handle_sigill(int signum)
42{
43 longjmp(env, 1);
44}
45
46unsigned long long stfle(void)
47{
48
49 unsigned long long ret;
50
51 signal(SIGILL, handle_sigill);
52 if (setjmp(env)) {
53 /* stfle not available: assume no facilities */
54 return 0;
55 } else {
56 asm volatile("lghi 0, 0\n"
57 ".insn s,0xb2b00000,%0\n" /* stfle */
58 : "=Q" (ret)::"0", "cc");
59 return ret;
60 }
61}
62
florianec82dd92011-07-07 22:09:41 +000063
64/* Read /proc/cpuinfo. Look for lines like these
65
66 processor 0: version = FF, identification = 0117C9, machine = 2064
67
68 and return the machine model or NULL on error.
69 Adapted from function VG_(get_machine_model) in coregrind/m_machine.c */
70
71typedef struct {
72 const char *cpuinfo_name;
73 const char *real_name;
74} model_info;
75
76/* Array needs to be sorted chronologically. Oldest to newest */
77model_info models[] = {
78 { "2064", "z900" },
79 { "2066", "z800" },
80 { "2084", "z990" },
81 { "2086", "z890" },
florian87a25cf2012-08-28 13:33:10 +000082 { "2094", "z9-EC" },
83 { "2096", "z9-BC" },
84 { "2097", "z10-EC" },
85 { "2098", "z10-BC" },
florianec82dd92011-07-07 22:09:41 +000086 { "2817", "z196" },
cborntra9dd35e02012-05-29 11:02:28 +000087 { "2818", "z114" },
florian87a25cf2012-08-28 13:33:10 +000088 { "2827", "zEC12" },
florianec82dd92011-07-07 22:09:41 +000089};
90
91
92/* Locate a machine model by name. Name can be either the cpuinfo
93 name or the external name. */
94static model_info *locate_model(const char *name)
95{
96 model_info *p;
97
98 /* Try cpuinfo name first */
99 for (p = models; p != models + sizeof models / sizeof models[0]; ++p) {
100 if (strcmp(p->cpuinfo_name, name) == 0) return p; // found it
101 }
102
103 /* Now try external name */
104 for (p = models; p != models + sizeof models / sizeof models[0]; ++p) {
105 if (strcmp(p->real_name, name) == 0) return p; // found it
106 }
107
108 return NULL;
109}
110
111
112static model_info *get_host(void)
113{
114 int n, fh;
115 size_t num_bytes, file_buf_size;
116 char *p, *m, *model_name, *file_buf;
117 model_info *model;
118
119 /* Slurp contents of /proc/cpuinfo into FILE_BUF */
florianec82dd92011-07-07 22:09:41 +0000120 fh = open("/proc/cpuinfo", O_RDONLY, S_IRUSR);
121 if (fh < 0) return NULL;
122
123 /* Determine the size of /proc/cpuinfo.
124 Work around broken-ness in /proc file system implementation.
125 fstat returns a zero size for /proc/cpuinfo although it is
126 claimed to be a regular file. */
127 num_bytes = 0;
128 file_buf_size = 1000;
129 file_buf = malloc(file_buf_size + 1);
130
131 while (42) {
132 n = read(fh, file_buf, file_buf_size);
133 if (n < 0) break;
134
135 num_bytes += n;
136 if (n < file_buf_size) break; /* reached EOF */
137 }
138
139 if (n < 0) num_bytes = 0; /* read error; ignore contents */
140
141 if (num_bytes > file_buf_size) {
142 free(file_buf);
143 lseek(fh, 0, SEEK_SET);
144 file_buf = malloc(num_bytes + 1);
145 n = read(fh, file_buf, num_bytes);
146 if (n < 0) num_bytes = 0;
147 }
148
149 file_buf[num_bytes] = '\0';
150 close(fh);
151
152 /* Parse file */
153 model = models + sizeof models / sizeof models[0];
154 for (p = file_buf; *p; ++p) {
155 /* Beginning of line */
156 if (strncmp(p, "processor", sizeof "processor" - 1 ) != 0) continue;
157
158 m = strstr(p, "machine");
159 if (m == NULL) continue;
160
161 p = m + sizeof "machine" - 1;
162 while (isspace(*p) || *p == '=') {
163 if (*p == '\n') goto next_line;
164 ++p;
165 }
166
167 model_name = p;
168 for (n = 0; n < sizeof models / sizeof models[0]; ++n) {
169 model_info *mm = models + n;
170 size_t len = strlen(mm->cpuinfo_name);
171 if (strncmp(mm->cpuinfo_name, model_name, len) == 0 &&
172 isspace(model_name[len])) {
173 /* In case there are different CPUs in this cluster return the
174 one with the dewest capabilities ("oldest" model). */
175 if (mm < model) model = mm;
176 p = model_name + len;
177 break;
178 }
179 }
180 /* Skip until end-of-line */
181 while (*p != '\n')
182 ++p;
183 next_line: ;
184 }
185
186 free(file_buf);
187
188 if (model == models + sizeof models / sizeof models[0]) return NULL;
189
190 return model;
191}
192
193static int go(char *feature, char *cpu)
sewardj02b89e82011-05-17 16:19:53 +0000194{
195 unsigned long long facilities;
196 unsigned long long match;
florianec82dd92011-07-07 22:09:41 +0000197 model_info *host, *from, *to, *p;
198 char *colon;
sewardj02b89e82011-05-17 16:19:53 +0000199
200 facilities = stfle();
201
florianec82dd92011-07-07 22:09:41 +0000202 if (strcmp(feature, "s390x-zarch") == 0 ) {
sewardj02b89e82011-05-17 16:19:53 +0000203 match = (facilities & (1ULL << 62) && (facilities & (1ULL << 61)));
florianec82dd92011-07-07 22:09:41 +0000204 } else if (strcmp(feature, "s390x-n3") == 0 ) {
sewardj02b89e82011-05-17 16:19:53 +0000205 match = (facilities & (1ULL << 63));
florianec82dd92011-07-07 22:09:41 +0000206 } else if (strcmp(feature, "s390x-stfle") == 0 ) {
sewardj02b89e82011-05-17 16:19:53 +0000207 match = (facilities & (1ULL << 56));
florianec82dd92011-07-07 22:09:41 +0000208 } else if (strcmp(feature, "s390x-ldisp") == 0 ) {
sewardj02b89e82011-05-17 16:19:53 +0000209 match = (facilities & (1ULL << 45) && (facilities & (1ULL << 44)));
florianec82dd92011-07-07 22:09:41 +0000210 } else if (strcmp(feature, "s390x-eimm") == 0 ) {
sewardj02b89e82011-05-17 16:19:53 +0000211 match = (facilities & (1ULL << 42));
florianec82dd92011-07-07 22:09:41 +0000212 } else if (strcmp(feature, "s390x-stckf") == 0 ) {
sewardj02b89e82011-05-17 16:19:53 +0000213 match = (facilities & (1ULL << 38));
florianec82dd92011-07-07 22:09:41 +0000214 } else if (strcmp(feature, "s390x-genins") == 0 ) {
sewardj02b89e82011-05-17 16:19:53 +0000215 match = (facilities & (1ULL << 29));
florianec82dd92011-07-07 22:09:41 +0000216 } else if (strcmp(feature, "s390x-exrl") == 0 ) {
sewardj02b89e82011-05-17 16:19:53 +0000217 match = (facilities & (1ULL << 28));
florian4b345f32012-07-19 14:35:55 +0000218 } else if (strcmp(feature, "s390x-etf3") == 0 ) {
florian1b6bca22012-07-19 18:16:49 +0000219 match = (facilities & (1ULL << (63 - 30)));
sewardj02b89e82011-05-17 16:19:53 +0000220 } else {
221 return 2; // Unrecognised feature.
222 }
223
florianec82dd92011-07-07 22:09:41 +0000224 if (match == 0) return 1; // facility not provided
225
226 /* Host provides facility. If no CPU was specified, we're done. */
227 if (cpu == NULL) return 0;
228
229 host = get_host();
cborntra9dd35e02012-05-29 11:02:28 +0000230 if (host == NULL) return 1; // unknown model
florianec82dd92011-07-07 22:09:41 +0000231
232 // printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name);
233
234 /* Determine interval of models in which to search for HOST. */
235 from = to = NULL;
236 colon = strchr(cpu, ':');
237
238 if (colon == NULL) {
239 // match exact
240 from = to = locate_model(cpu);
241 } else if (colon == cpu) {
242 // :NAME match machines up to and including CPU
243 from = models;
244 to = locate_model(cpu + 1);
245 } else if (colon[1] == '\0') {
246 // NAME: match machines beginning with CPU or later
247 *colon = '\0';
248 from = locate_model(cpu);
249 to = models + sizeof models / sizeof models[0] - 1;
250 *colon = ':';
251 } else {
252 // NAME:NAME match machines in interval
253 *colon = '\0';
254 from = locate_model(cpu);
255 to = locate_model(colon + 1);
256 *colon = ':';
257 }
258
259 if (from == NULL || to == NULL || from > to) {
260 fprintf(stderr, "invalid cpu specification '%s'\n", cpu);
261 return 3;
262 }
263
264#if 0
265 printf("from %s (%s) to %s (%s)\n", from->cpuinfo_name, from->real_name,
266 to->cpuinfo_name, to->real_name);
267#endif
268
269 /* Search for HOST. */
270 for (p = from; p <= to; ++p) {
271 if (p == host) return 0;
272 }
273
274 return 1; // host does not match CPU specification
sewardj02b89e82011-05-17 16:19:53 +0000275}
276
277#else
278
florianec82dd92011-07-07 22:09:41 +0000279static int go(char *feature, char *cpu)
sewardj02b89e82011-05-17 16:19:53 +0000280{
281 return 2; // Feature not recognised (non-s390x machine!)
282}
283
284#endif
285
286
287//---------------------------------------------------------------------------
288// main
289//---------------------------------------------------------------------------
290int main(int argc, char **argv)
291{
florianec82dd92011-07-07 22:09:41 +0000292 int rc;
293
294 if (argc < 2 || argc > 3) {
295 fprintf( stderr, "usage: s390x_features <feature> [<machine-model>]\n" );
sewardj02b89e82011-05-17 16:19:53 +0000296 exit(3); // Usage error.
297 }
florianec82dd92011-07-07 22:09:41 +0000298
299 rc = go(argv[1], argv[2]);
300
301 // printf("rc = %d\n", rc);
302
303 return rc;
sewardj02b89e82011-05-17 16:19:53 +0000304}