• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <ctype.h>     // isspace
9 #include <fcntl.h>     // open
10 #include <unistd.h>    // lseek
11 #include <sys/stat.h>  // S_IRUSR
12 
13 // This file determines s390x features a processor supports.
14 //
15 // We return:
16 // - 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
20 // - 2 if the asked-for feature isn't recognised (this will be the case for
21 //     any feature if run on a non-s390x machine).
22 // - 2 for an unknown cpu model in /proc/cpu_info
23 // - 3 if there was a usage error (it also prints an error message).
24 //
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.
36 
37 jmp_buf env;
38 
39 #if defined(VGA_s390x)
40 
handle_sigill(int signum)41 void handle_sigill(int signum)
42 {
43    longjmp(env, 1);
44 }
45 
stfle(void)46 unsigned 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 
63 
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 
71 typedef 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 */
77 model_info models[] = {
78    { "2064", "z900"   },
79    { "2066", "z800"   },
80    { "2084", "z990"   },
81    { "2086", "z890"   },
82    { "2094", "z9-ec"  },
83    { "2096", "z9-bc"  },
84    { "2097", "z10-ec" },
85    { "2098", "z10-bc" },
86    { "2817", "z196"   },
87 };
88 
89 
90 /* Locate a machine model by name. Name can be either the cpuinfo
91    name or the external name. */
locate_model(const char * name)92 static model_info *locate_model(const char *name)
93 {
94    model_info *p;
95 
96    /* Try cpuinfo name first */
97    for (p = models; p != models + sizeof models / sizeof models[0]; ++p) {
98       if (strcmp(p->cpuinfo_name, name) == 0) return p;  // found it
99    }
100 
101    /* Now try external name */
102    for (p = models; p != models + sizeof models / sizeof models[0]; ++p) {
103       if (strcmp(p->real_name, name) == 0) return p;  // found it
104    }
105 
106    return NULL;
107 }
108 
109 
get_host(void)110 static model_info *get_host(void)
111 {
112    int    n, fh;
113    size_t num_bytes, file_buf_size;
114    char  *p, *m, *model_name, *file_buf;
115    model_info *model;
116 
117    /* Slurp contents of /proc/cpuinfo into FILE_BUF */
118    //fh = open("/proc/cpuinfo", O_RDONLY, S_IRUSR);
119    fh = open("/proc/cpuinfo", O_RDONLY, S_IRUSR);
120    if (fh < 0) return NULL;
121 
122    /* Determine the size of /proc/cpuinfo.
123       Work around broken-ness in /proc file system implementation.
124       fstat returns a zero size for /proc/cpuinfo although it is
125       claimed to be a regular file. */
126    num_bytes = 0;
127    file_buf_size = 1000;
128    file_buf = malloc(file_buf_size + 1);
129 
130    while (42) {
131       n = read(fh, file_buf, file_buf_size);
132       if (n < 0) break;
133 
134       num_bytes += n;
135       if (n < file_buf_size) break;  /* reached EOF */
136    }
137 
138    if (n < 0) num_bytes = 0;   /* read error; ignore contents */
139 
140    if (num_bytes > file_buf_size) {
141       free(file_buf);
142       lseek(fh, 0, SEEK_SET);
143       file_buf = malloc(num_bytes + 1);
144       n = read(fh, file_buf, num_bytes);
145       if (n < 0) num_bytes = 0;
146    }
147 
148    file_buf[num_bytes] = '\0';
149    close(fh);
150 
151    /* Parse file */
152    model = models + sizeof models / sizeof models[0];
153    for (p = file_buf; *p; ++p) {
154       /* Beginning of line */
155       if (strncmp(p, "processor", sizeof "processor" - 1 ) != 0) continue;
156 
157       m = strstr(p, "machine");
158       if (m == NULL) continue;
159 
160       p = m + sizeof "machine" - 1;
161       while (isspace(*p) || *p == '=') {
162          if (*p == '\n') goto next_line;
163          ++p;
164       }
165 
166       model_name = p;
167       for (n = 0; n < sizeof models / sizeof models[0]; ++n) {
168          model_info *mm = models + n;
169          size_t len = strlen(mm->cpuinfo_name);
170          if (strncmp(mm->cpuinfo_name, model_name, len) == 0 &&
171              isspace(model_name[len])) {
172             /* In case there are different CPUs in this cluster return the
173                one with the dewest capabilities ("oldest" model). */
174             if (mm < model) model = mm;
175             p = model_name + len;
176             break;
177          }
178       }
179       /* Skip until end-of-line */
180       while (*p != '\n')
181          ++p;
182    next_line: ;
183    }
184 
185    free(file_buf);
186 
187    if (model == models + sizeof models / sizeof models[0]) return NULL;
188 
189    return model;
190 }
191 
go(char * feature,char * cpu)192 static int go(char *feature, char *cpu)
193 {
194    unsigned long long facilities;
195    unsigned long long match;
196    model_info *host, *from, *to, *p;
197    char *colon;
198 
199    facilities = stfle();
200 
201    if        (strcmp(feature, "s390x-zarch") == 0 ) {
202      match = (facilities & (1ULL << 62) && (facilities & (1ULL << 61)));
203    } else if (strcmp(feature, "s390x-n3") == 0 ) {
204      match = (facilities & (1ULL << 63));
205    } else if (strcmp(feature, "s390x-stfle") == 0 ) {
206      match = (facilities & (1ULL << 56));
207    } else if (strcmp(feature, "s390x-ldisp") == 0 ) {
208      match = (facilities & (1ULL << 45) && (facilities & (1ULL << 44)));
209    } else if (strcmp(feature, "s390x-eimm") == 0 ) {
210      match = (facilities & (1ULL << 42));
211    } else if (strcmp(feature, "s390x-stckf") == 0 ) {
212      match = (facilities & (1ULL << 38));
213    } else if (strcmp(feature, "s390x-genins") == 0 ) {
214      match = (facilities & (1ULL << 29));
215    } else if (strcmp(feature, "s390x-exrl") == 0 ) {
216      match = (facilities & (1ULL << 28));
217    } else {
218      return 2;          // Unrecognised feature.
219    }
220 
221    if (match == 0) return 1;   // facility not provided
222 
223    /* Host provides facility. If no CPU was specified, we're done. */
224    if (cpu == NULL) return 0;
225 
226    host = get_host();
227    if (host == NULL) return 2;  // unknown model
228 
229    //   printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name);
230 
231    /* Determine interval of models in which to search for HOST. */
232    from = to = NULL;
233    colon = strchr(cpu, ':');
234 
235    if (colon == NULL) {
236       // match exact
237       from = to = locate_model(cpu);
238    } else if (colon == cpu) {
239       // :NAME  match machines up to and including CPU
240       from = models;
241       to   = locate_model(cpu + 1);
242    } else if (colon[1] == '\0') {
243       // NAME:  match machines beginning with CPU or later
244       *colon = '\0';
245       from = locate_model(cpu);
246       to   = models + sizeof models / sizeof models[0] - 1;
247       *colon = ':';
248    } else {
249       // NAME:NAME  match machines in interval
250       *colon = '\0';
251       from = locate_model(cpu);
252       to   = locate_model(colon + 1);
253       *colon = ':';
254    }
255 
256    if (from == NULL || to == NULL || from > to) {
257       fprintf(stderr, "invalid cpu specification '%s'\n", cpu);
258       return 3;
259    }
260 
261 #if 0
262    printf("from  %s (%s)  to  %s (%s)\n", from->cpuinfo_name, from->real_name,
263           to->cpuinfo_name, to->real_name);
264 #endif
265 
266    /* Search for HOST. */
267    for (p = from; p <= to; ++p) {
268       if (p == host) return 0;
269    }
270 
271    return 1; // host does not match CPU specification
272 }
273 
274 #else
275 
go(char * feature,char * cpu)276 static int go(char *feature, char *cpu)
277 {
278    return 2;      // Feature not recognised (non-s390x machine!)
279 }
280 
281 #endif
282 
283 
284 //---------------------------------------------------------------------------
285 // main
286 //---------------------------------------------------------------------------
main(int argc,char ** argv)287 int main(int argc, char **argv)
288 {
289    int rc;
290 
291    if (argc < 2 || argc > 3) {
292       fprintf( stderr, "usage: s390x_features <feature> [<machine-model>]\n" );
293       exit(3);                // Usage error.
294    }
295 
296    rc = go(argv[1], argv[2]);
297 
298    //   printf("rc = %d\n", rc);
299 
300    return rc;
301 }
302