1 /* -*- mode: C; c-basic-offset: 3; -*- */
2
3 #include <setjmp.h>
4 #include <signal.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9 #include <ctype.h> // isspace
10 #include <fcntl.h> // open
11 #include <unistd.h> // lseek
12 #include <sys/stat.h> // S_IRUSR
13
14 // This file determines s390x features a processor supports.
15 //
16 // We return:
17 // - 0 if the machine provides the asked-for feature and the cpu
18 // model, if specified, matches the machine
19 // - 1 the machine does not provide the asked-for feature or the
20 // cpu model, if specified, does not match the machine
21 // - 1 for an unknown cpu model in /proc/cpu_info
22 // - 2 if the asked-for feature isn't recognised (this will be the case for
23 // any feature if run on a non-s390x machine).
24 // - 3 if there was a usage error (it also prints an error message).
25 //
26 // USAGE:
27 //
28 // s390x_features <feature> [<machine-model>]
29 //
30 // The machine_model is optional and it can be something like:
31 //
32 // z9 -- Host needs to be a z9 (and nothing else)
33 // z9: -- Host needs to be a z9 or any later model
34 // :z9 -- Host needs to be a model up to and including z9
35 // z900:z9 -- Host needs to be at least a z900 and at most a z9.
36 // Any model in between is OK, too.
37
38 jmp_buf env;
39
40 #if defined(VGA_s390x)
41
handle_sigill(int signum)42 void handle_sigill(int signum)
43 {
44 longjmp(env, 1);
45 }
46
stfle(void)47 unsigned long long stfle(void)
48 {
49
50 unsigned long long ret;
51
52 signal(SIGILL, handle_sigill);
53 if (setjmp(env)) {
54 /* stfle not available: assume no facilities */
55 return 0;
56 } else {
57 asm volatile("lghi 0, 0\n"
58 ".insn s,0xb2b00000,%0\n" /* stfle */
59 : "=Q" (ret)::"0", "cc");
60 return ret;
61 }
62 }
63
64
65 /* Read /proc/cpuinfo. Look for lines like these
66
67 processor 0: version = FF, identification = 0117C9, machine = 2064
68
69 and return the machine model or NULL on error.
70 Adapted from function VG_(get_machine_model) in coregrind/m_machine.c */
71
72 typedef struct {
73 const char *cpuinfo_name;
74 const char *real_name;
75 } model_info;
76
77 /* Array needs to be sorted chronologically. Oldest to newest */
78 model_info models[] = {
79 { "2064", "z900" },
80 { "2066", "z800" },
81 { "2084", "z990" },
82 { "2086", "z890" },
83 { "2094", "z9-EC" },
84 { "2096", "z9-BC" },
85 { "2097", "z10-EC" },
86 { "2098", "z10-BC" },
87 { "2817", "z196" },
88 { "2818", "z114" },
89 { "2827", "zEC12" },
90 { "2828", "zBC12" },
91 { "2964", "z13" },
92 { "2965", "z13s" },
93 };
94
95
96 /* Locate a machine model by name. Name can be either the cpuinfo
97 name or the external name. */
locate_model(const char * name)98 static model_info *locate_model(const char *name)
99 {
100 model_info *p;
101
102 /* Try cpuinfo name first */
103 for (p = models; p != models + sizeof models / sizeof models[0]; ++p) {
104 if (strcmp(p->cpuinfo_name, name) == 0) return p; // found it
105 }
106
107 /* Now try external name */
108 for (p = models; p != models + sizeof models / sizeof models[0]; ++p) {
109 if (strcmp(p->real_name, name) == 0) return p; // found it
110 }
111
112 return NULL;
113 }
114
115
get_host(void)116 static model_info *get_host(void)
117 {
118 int n, fh;
119 size_t num_bytes, file_buf_size;
120 char *p, *m, *model_name, *file_buf;
121 model_info *model;
122
123 /* Slurp contents of /proc/cpuinfo into FILE_BUF */
124 fh = open("/proc/cpuinfo", O_RDONLY, S_IRUSR);
125 if (fh < 0) return NULL;
126
127 /* Determine the size of /proc/cpuinfo.
128 Work around broken-ness in /proc file system implementation.
129 fstat returns a zero size for /proc/cpuinfo although it is
130 claimed to be a regular file. */
131 num_bytes = 0;
132 file_buf_size = 1000;
133 file_buf = malloc(file_buf_size + 1);
134
135 while (42) {
136 n = read(fh, file_buf, file_buf_size);
137 if (n < 0) break;
138
139 num_bytes += n;
140 if (n < file_buf_size) break; /* reached EOF */
141 }
142
143 if (n < 0) num_bytes = 0; /* read error; ignore contents */
144
145 if (num_bytes > file_buf_size) {
146 free(file_buf);
147 lseek(fh, 0, SEEK_SET);
148 file_buf = malloc(num_bytes + 1);
149 n = read(fh, file_buf, num_bytes);
150 if (n < 0) num_bytes = 0;
151 }
152
153 file_buf[num_bytes] = '\0';
154 close(fh);
155
156 /* Parse file */
157 model = models + sizeof models / sizeof models[0];
158 for (p = file_buf; *p; ++p) {
159 /* Beginning of line */
160 if (strncmp(p, "processor", sizeof "processor" - 1 ) != 0) continue;
161
162 m = strstr(p, "machine");
163 if (m == NULL) continue;
164
165 p = m + sizeof "machine" - 1;
166 while (isspace(*p) || *p == '=') {
167 if (*p == '\n') goto next_line;
168 ++p;
169 }
170
171 model_name = p;
172 for (n = 0; n < sizeof models / sizeof models[0]; ++n) {
173 model_info *mm = models + n;
174 size_t len = strlen(mm->cpuinfo_name);
175 if (strncmp(mm->cpuinfo_name, model_name, len) == 0 &&
176 isspace(model_name[len])) {
177 /* In case there are different CPUs in this cluster return the
178 one with the dewest capabilities ("oldest" model). */
179 if (mm < model) model = mm;
180 p = model_name + len;
181 break;
182 }
183 }
184 /* Skip until end-of-line */
185 while (*p != '\n')
186 ++p;
187 next_line: ;
188 }
189
190 free(file_buf);
191
192 if (model == models + sizeof models / sizeof models[0]) return NULL;
193
194 return model;
195 }
196
197
198 /* Convenience macro that maps the facility bit number as given in the
199 Principles of Ops "facility indications" section to a bit mask */
200 #define FAC_BIT(x) (1ULL << (63 - (x)))
201
go(char * feature,char * cpu)202 static int go(char *feature, char *cpu)
203 {
204 unsigned long long facilities;
205 unsigned long long match;
206 model_info *host, *from, *to, *p;
207 char *colon;
208
209 facilities = stfle();
210
211 if (strcmp(feature, "s390x-zarch") == 0 ) {
212 match = (facilities & FAC_BIT(1)) && (facilities & FAC_BIT(2));
213 } else if (strcmp(feature, "s390x-n3") == 0 ) {
214 match = facilities & FAC_BIT(0);
215 } else if (strcmp(feature, "s390x-stfle") == 0 ) {
216 match = facilities & FAC_BIT(7);
217 } else if (strcmp(feature, "s390x-ldisp") == 0 ) {
218 match = (facilities & FAC_BIT(18)) && (facilities & FAC_BIT(19));
219 } else if (strcmp(feature, "s390x-eimm") == 0 ) {
220 match = facilities & FAC_BIT(21);
221 } else if (strcmp(feature, "s390x-stckf") == 0 ) {
222 match = facilities & FAC_BIT(25);
223 } else if (strcmp(feature, "s390x-genins") == 0 ) {
224 match = facilities & FAC_BIT(34);
225 } else if (strcmp(feature, "s390x-exrl") == 0 ) {
226 match = facilities & FAC_BIT(35);
227 } else if (strcmp(feature, "s390x-etf3") == 0 ) {
228 match = facilities & FAC_BIT(30);
229 } else if (strcmp(feature, "s390x-fpext") == 0 ) {
230 match = facilities & FAC_BIT(37);
231 } else if (strcmp(feature, "s390x-dfp") == 0 ) {
232 match = facilities & FAC_BIT(42);
233 } else if (strcmp(feature, "s390x-pfpo") == 0 ) {
234 match = facilities & FAC_BIT(44);
235 } else if (strcmp(feature, "s390x-highw") == 0 ) {
236 match = facilities & FAC_BIT(45);
237 } else {
238 return 2; // Unrecognised feature.
239 }
240
241 if (match == 0) return 1; // facility not provided
242
243 /* Host provides facility. If no CPU was specified, we're done. */
244 if (cpu == NULL) return 0;
245
246 host = get_host();
247 if (host == NULL) return 1; // unknown model
248
249 // printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name);
250
251 /* Determine interval of models in which to search for HOST. */
252 from = to = NULL;
253 colon = strchr(cpu, ':');
254
255 if (colon == NULL) {
256 // match exact
257 from = to = locate_model(cpu);
258 } else if (colon == cpu) {
259 // :NAME match machines up to and including CPU
260 from = models;
261 to = locate_model(cpu + 1);
262 } else if (colon[1] == '\0') {
263 // NAME: match machines beginning with CPU or later
264 *colon = '\0';
265 from = locate_model(cpu);
266 to = models + sizeof models / sizeof models[0] - 1;
267 *colon = ':';
268 } else {
269 // NAME:NAME match machines in interval
270 *colon = '\0';
271 from = locate_model(cpu);
272 to = locate_model(colon + 1);
273 *colon = ':';
274 }
275
276 if (from == NULL || to == NULL || from > to) {
277 fprintf(stderr, "invalid cpu specification '%s'\n", cpu);
278 return 3;
279 }
280
281 #if 0
282 printf("from %s (%s) to %s (%s)\n", from->cpuinfo_name, from->real_name,
283 to->cpuinfo_name, to->real_name);
284 #endif
285
286 /* Search for HOST. */
287 for (p = from; p <= to; ++p) {
288 if (p == host) return 0;
289 }
290
291 return 1; // host does not match CPU specification
292 }
293
294 #else
295
go(char * feature,char * cpu)296 static int go(char *feature, char *cpu)
297 {
298 return 2; // Feature not recognised (non-s390x machine!)
299 }
300
301 #endif
302
303
304 //---------------------------------------------------------------------------
305 // main
306 //---------------------------------------------------------------------------
main(int argc,char ** argv)307 int main(int argc, char **argv)
308 {
309 int rc, inverted = 0;
310
311 if (argc < 2 || argc > 3) {
312 fprintf( stderr, "usage: s390x_features <feature> [<machine-model>]\n" );
313 exit(3); // Usage error.
314 }
315
316 if (argv[1][0] == '!') {
317 assert(argv[2] == NULL); // not allowed
318 inverted = 1;
319 ++argv[1];
320 }
321
322 rc = go(argv[1], argv[2]);
323
324 if (inverted) {
325 switch (rc) {
326 case 0: rc = 1; break;
327 case 1: rc = 0; break;
328 case 2: rc = 2; break;
329 }
330 }
331
332 // printf("rc = %d\n", rc);
333
334 return rc;
335 }
336