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