blob: 4dde355f2994ec8de2e27d421b6626cbb96fbe08 [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>
floriana066ccd2012-09-02 03:30:38 +00008#include <assert.h>
florianec82dd92011-07-07 22:09:41 +00009#include <ctype.h> // isspace
10#include <fcntl.h> // open
11#include <unistd.h> // lseek
12#include <sys/stat.h> // S_IRUSR
sewardj02b89e82011-05-17 16:19:53 +000013
14// This file determines s390x features a processor supports.
15//
16// We return:
florianec82dd92011-07-07 22:09:41 +000017// - 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
cborntra9dd35e02012-05-29 11:02:28 +000021// - 1 for an unknown cpu model in /proc/cpu_info
sewardj02b89e82011-05-17 16:19:53 +000022// - 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).
florianec82dd92011-07-07 22:09:41 +000025//
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.
sewardj02b89e82011-05-17 16:19:53 +000037
38jmp_buf env;
39
40#if defined(VGA_s390x)
41
42void handle_sigill(int signum)
43{
44 longjmp(env, 1);
45}
46
47unsigned 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
florianec82dd92011-07-07 22:09:41 +000064
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
72typedef 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 */
78model_info models[] = {
79 { "2064", "z900" },
80 { "2066", "z800" },
81 { "2084", "z990" },
82 { "2086", "z890" },
florian87a25cf2012-08-28 13:33:10 +000083 { "2094", "z9-EC" },
84 { "2096", "z9-BC" },
85 { "2097", "z10-EC" },
86 { "2098", "z10-BC" },
florianec82dd92011-07-07 22:09:41 +000087 { "2817", "z196" },
cborntra9dd35e02012-05-29 11:02:28 +000088 { "2818", "z114" },
florian87a25cf2012-08-28 13:33:10 +000089 { "2827", "zEC12" },
florianec82dd92011-07-07 22:09:41 +000090};
91
92
93/* Locate a machine model by name. Name can be either the cpuinfo
94 name or the external name. */
95static model_info *locate_model(const char *name)
96{
97 model_info *p;
98
99 /* Try cpuinfo name first */
100 for (p = models; p != models + sizeof models / sizeof models[0]; ++p) {
101 if (strcmp(p->cpuinfo_name, name) == 0) return p; // found it
102 }
103
104 /* Now try external name */
105 for (p = models; p != models + sizeof models / sizeof models[0]; ++p) {
106 if (strcmp(p->real_name, name) == 0) return p; // found it
107 }
108
109 return NULL;
110}
111
112
113static model_info *get_host(void)
114{
115 int n, fh;
116 size_t num_bytes, file_buf_size;
117 char *p, *m, *model_name, *file_buf;
118 model_info *model;
119
120 /* Slurp contents of /proc/cpuinfo into FILE_BUF */
florianec82dd92011-07-07 22:09:41 +0000121 fh = open("/proc/cpuinfo", O_RDONLY, S_IRUSR);
122 if (fh < 0) return NULL;
123
124 /* Determine the size of /proc/cpuinfo.
125 Work around broken-ness in /proc file system implementation.
126 fstat returns a zero size for /proc/cpuinfo although it is
127 claimed to be a regular file. */
128 num_bytes = 0;
129 file_buf_size = 1000;
130 file_buf = malloc(file_buf_size + 1);
131
132 while (42) {
133 n = read(fh, file_buf, file_buf_size);
134 if (n < 0) break;
135
136 num_bytes += n;
137 if (n < file_buf_size) break; /* reached EOF */
138 }
139
140 if (n < 0) num_bytes = 0; /* read error; ignore contents */
141
142 if (num_bytes > file_buf_size) {
143 free(file_buf);
144 lseek(fh, 0, SEEK_SET);
145 file_buf = malloc(num_bytes + 1);
146 n = read(fh, file_buf, num_bytes);
147 if (n < 0) num_bytes = 0;
148 }
149
150 file_buf[num_bytes] = '\0';
151 close(fh);
152
153 /* Parse file */
154 model = models + sizeof models / sizeof models[0];
155 for (p = file_buf; *p; ++p) {
156 /* Beginning of line */
157 if (strncmp(p, "processor", sizeof "processor" - 1 ) != 0) continue;
158
159 m = strstr(p, "machine");
160 if (m == NULL) continue;
161
162 p = m + sizeof "machine" - 1;
163 while (isspace(*p) || *p == '=') {
164 if (*p == '\n') goto next_line;
165 ++p;
166 }
167
168 model_name = p;
169 for (n = 0; n < sizeof models / sizeof models[0]; ++n) {
170 model_info *mm = models + n;
171 size_t len = strlen(mm->cpuinfo_name);
172 if (strncmp(mm->cpuinfo_name, model_name, len) == 0 &&
173 isspace(model_name[len])) {
174 /* In case there are different CPUs in this cluster return the
175 one with the dewest capabilities ("oldest" model). */
176 if (mm < model) model = mm;
177 p = model_name + len;
178 break;
179 }
180 }
181 /* Skip until end-of-line */
182 while (*p != '\n')
183 ++p;
184 next_line: ;
185 }
186
187 free(file_buf);
188
189 if (model == models + sizeof models / sizeof models[0]) return NULL;
190
191 return model;
192}
193
florian4bc35a82012-08-30 18:47:56 +0000194
195/* Convenience macro that maps the facility bit number as given in the
196 Principles of Ops "facility indications" section to a bit mask */
197#define FAC_BIT(x) (1ULL << (63 - (x)))
198
florianec82dd92011-07-07 22:09:41 +0000199static int go(char *feature, char *cpu)
sewardj02b89e82011-05-17 16:19:53 +0000200{
201 unsigned long long facilities;
202 unsigned long long match;
florianec82dd92011-07-07 22:09:41 +0000203 model_info *host, *from, *to, *p;
204 char *colon;
sewardj02b89e82011-05-17 16:19:53 +0000205
206 facilities = stfle();
207
florianec82dd92011-07-07 22:09:41 +0000208 if (strcmp(feature, "s390x-zarch") == 0 ) {
florian4bc35a82012-08-30 18:47:56 +0000209 match = (facilities & FAC_BIT(1)) && (facilities & FAC_BIT(2));
florianec82dd92011-07-07 22:09:41 +0000210 } else if (strcmp(feature, "s390x-n3") == 0 ) {
florian4bc35a82012-08-30 18:47:56 +0000211 match = facilities & FAC_BIT(0);
florianec82dd92011-07-07 22:09:41 +0000212 } else if (strcmp(feature, "s390x-stfle") == 0 ) {
florian4bc35a82012-08-30 18:47:56 +0000213 match = facilities & FAC_BIT(7);
florianec82dd92011-07-07 22:09:41 +0000214 } else if (strcmp(feature, "s390x-ldisp") == 0 ) {
florian4bc35a82012-08-30 18:47:56 +0000215 match = (facilities & FAC_BIT(18)) && (facilities & FAC_BIT(19));
florianec82dd92011-07-07 22:09:41 +0000216 } else if (strcmp(feature, "s390x-eimm") == 0 ) {
florian4bc35a82012-08-30 18:47:56 +0000217 match = facilities & FAC_BIT(21);
florianec82dd92011-07-07 22:09:41 +0000218 } else if (strcmp(feature, "s390x-stckf") == 0 ) {
florian4bc35a82012-08-30 18:47:56 +0000219 match = facilities & FAC_BIT(25);
florianec82dd92011-07-07 22:09:41 +0000220 } else if (strcmp(feature, "s390x-genins") == 0 ) {
florian4bc35a82012-08-30 18:47:56 +0000221 match = facilities & FAC_BIT(34);
florianec82dd92011-07-07 22:09:41 +0000222 } else if (strcmp(feature, "s390x-exrl") == 0 ) {
florian4bc35a82012-08-30 18:47:56 +0000223 match = facilities & FAC_BIT(35);
florian4b345f32012-07-19 14:35:55 +0000224 } else if (strcmp(feature, "s390x-etf3") == 0 ) {
florian4bc35a82012-08-30 18:47:56 +0000225 match = facilities & FAC_BIT(30);
florian370bc772012-08-30 20:30:32 +0000226 } else if (strcmp(feature, "s390x-fpext") == 0 ) {
227 match = facilities & FAC_BIT(37);
sewardj02b89e82011-05-17 16:19:53 +0000228 } else {
florian4bc35a82012-08-30 18:47:56 +0000229 return 2; // Unrecognised feature.
sewardj02b89e82011-05-17 16:19:53 +0000230 }
231
florianec82dd92011-07-07 22:09:41 +0000232 if (match == 0) return 1; // facility not provided
233
234 /* Host provides facility. If no CPU was specified, we're done. */
235 if (cpu == NULL) return 0;
236
237 host = get_host();
cborntra9dd35e02012-05-29 11:02:28 +0000238 if (host == NULL) return 1; // unknown model
florianec82dd92011-07-07 22:09:41 +0000239
240 // printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name);
241
242 /* Determine interval of models in which to search for HOST. */
243 from = to = NULL;
244 colon = strchr(cpu, ':');
245
246 if (colon == NULL) {
247 // match exact
248 from = to = locate_model(cpu);
249 } else if (colon == cpu) {
250 // :NAME match machines up to and including CPU
251 from = models;
252 to = locate_model(cpu + 1);
253 } else if (colon[1] == '\0') {
254 // NAME: match machines beginning with CPU or later
255 *colon = '\0';
256 from = locate_model(cpu);
257 to = models + sizeof models / sizeof models[0] - 1;
258 *colon = ':';
259 } else {
260 // NAME:NAME match machines in interval
261 *colon = '\0';
262 from = locate_model(cpu);
263 to = locate_model(colon + 1);
264 *colon = ':';
265 }
266
267 if (from == NULL || to == NULL || from > to) {
268 fprintf(stderr, "invalid cpu specification '%s'\n", cpu);
269 return 3;
270 }
271
272#if 0
273 printf("from %s (%s) to %s (%s)\n", from->cpuinfo_name, from->real_name,
274 to->cpuinfo_name, to->real_name);
275#endif
276
277 /* Search for HOST. */
278 for (p = from; p <= to; ++p) {
279 if (p == host) return 0;
280 }
281
282 return 1; // host does not match CPU specification
sewardj02b89e82011-05-17 16:19:53 +0000283}
284
285#else
286
florianec82dd92011-07-07 22:09:41 +0000287static int go(char *feature, char *cpu)
sewardj02b89e82011-05-17 16:19:53 +0000288{
289 return 2; // Feature not recognised (non-s390x machine!)
290}
291
292#endif
293
294
295//---------------------------------------------------------------------------
296// main
297//---------------------------------------------------------------------------
298int main(int argc, char **argv)
299{
floriana066ccd2012-09-02 03:30:38 +0000300 int rc, inverted = 0;
florianec82dd92011-07-07 22:09:41 +0000301
302 if (argc < 2 || argc > 3) {
303 fprintf( stderr, "usage: s390x_features <feature> [<machine-model>]\n" );
sewardj02b89e82011-05-17 16:19:53 +0000304 exit(3); // Usage error.
305 }
florianec82dd92011-07-07 22:09:41 +0000306
floriana066ccd2012-09-02 03:30:38 +0000307 if (argv[1][0] == '!') {
308 assert(argv[2] == NULL); // not allowed
309 inverted = 1;
310 ++argv[1];
311 }
312
florianec82dd92011-07-07 22:09:41 +0000313 rc = go(argv[1], argv[2]);
floriana066ccd2012-09-02 03:30:38 +0000314
315 if (inverted) {
316 switch (rc) {
317 case 0: rc = 1; break;
318 case 1: rc = 0; break;
319 case 2: rc = 2; break;
320 }
321 }
florianec82dd92011-07-07 22:09:41 +0000322
323 // printf("rc = %d\n", rc);
324
325 return rc;
sewardj02b89e82011-05-17 16:19:53 +0000326}