1 /* -*- mode: C; c-basic-offset: 3; -*- */
9 #include <ctype.h> // isspace
10 #include <fcntl.h> // open
11 #include <unistd.h> // lseek
12 #include <sys/stat.h> // S_IRUSR
14 // <features.h> is a glibc-specific extension, other libc's may not provide it
16 #include <features.h> // __GLIBC_PREREQ
19 // This file determines s390x features a processor supports.
22 // - 0 if the machine provides the asked-for feature and the cpu
23 // model, if specified, matches the machine
24 // - 1 the machine does not provide the asked-for feature or the
25 // cpu model, if specified, does not match the machine
26 // - 1 for an unknown cpu model in /proc/cpu_info
27 // - 2 if the asked-for feature isn't recognised (this will be the case for
28 // any feature if run on a non-s390x machine).
29 // - 3 if there was a usage error (it also prints an error message).
33 // s390x_features <feature> [<machine-model>]
35 // The machine_model is optional and it can be something like:
37 // z9 -- Host needs to be a z9 (and nothing else)
38 // z9: -- Host needs to be a z9 or any later model
39 // :z9 -- Host needs to be a model up to and including z9
40 // z900:z9 -- Host needs to be at least a z900 and at most a z9.
41 // Any model in between is OK, too.
45 #if defined(VGA_s390x)
47 // Features that require kernel support should be checked against HWCAP instead
48 // of the CPU facility list. To read the HWCAP, use 'getauxval' if available --
49 // which should be the case with glibc versions >= 2.16. A system with an older
50 // glibc is unlikely to support any of these features anyhow.
51 #if __GLIBC_PREREQ(2, 16)
53 #define GET_HWCAP() getauxval(AT_HWCAP)
55 #define GET_HWCAP() 0UL
58 /* Number of double words needed to store all facility bits. */
59 #define S390_NUM_FACILITY_DW 3
61 void handle_sigill(int signum
)
66 static void clear_facilities(unsigned long long *ret
)
69 for(index
= 0; index
< S390_NUM_FACILITY_DW
; index
++)
75 void stfle(unsigned long long *ret
)
77 signal(SIGILL
, handle_sigill
);
79 /* stfle not available: assume no facilities */
80 clear_facilities(ret
);
82 register unsigned long long r0
asm("0") = S390_NUM_FACILITY_DW
- 1;
83 asm volatile(".insn s,0xb2b00000,%0\n" /* stfle */
84 : "=m" (*ret
), "+d"(r0
) :: "cc", "memory");
89 /* Read /proc/cpuinfo. Look for lines like these
91 processor 0: version = FF, identification = 0117C9, machine = 2064
93 and return the machine model or NULL on error.
94 Adapted from function VG_(get_machine_model) in coregrind/m_machine.c */
97 const char *cpuinfo_name
;
98 const char *real_name
;
101 /* Array needs to be sorted chronologically. Oldest to newest */
102 model_info models
[] = {
109 { "2097", "z10-EC" },
110 { "2098", "z10-BC" },
118 { "3907", "z14 ZR1"},
126 /* Locate a machine model by name. Name can be either the cpuinfo
127 name or the external name. */
128 static model_info
*locate_model(const char *name
)
132 /* Try cpuinfo name first */
133 for (p
= models
; p
!= models
+ sizeof models
/ sizeof models
[0]; ++p
) {
134 if (strcmp(p
->cpuinfo_name
, name
) == 0) return p
; // found it
137 /* Now try external name */
138 for (p
= models
; p
!= models
+ sizeof models
/ sizeof models
[0]; ++p
) {
139 if (strcmp(p
->real_name
, name
) == 0) return p
; // found it
146 static model_info
*get_host(void)
149 size_t num_bytes
, file_buf_size
;
150 char *p
, *m
, *model_name
, *file_buf
;
153 /* Slurp contents of /proc/cpuinfo into FILE_BUF */
154 fh
= open("/proc/cpuinfo", O_RDONLY
, S_IRUSR
);
155 if (fh
< 0) return NULL
;
157 /* Determine the size of /proc/cpuinfo.
158 Work around broken-ness in /proc file system implementation.
159 fstat returns a zero size for /proc/cpuinfo although it is
160 claimed to be a regular file. */
162 file_buf_size
= 1000;
163 file_buf
= malloc(file_buf_size
+ 1);
166 n
= read(fh
, file_buf
, file_buf_size
);
170 if (n
< file_buf_size
) break; /* reached EOF */
173 if (n
< 0) num_bytes
= 0; /* read error; ignore contents */
175 if (num_bytes
> file_buf_size
) {
177 lseek(fh
, 0, SEEK_SET
);
178 file_buf
= malloc(num_bytes
+ 1);
179 n
= read(fh
, file_buf
, num_bytes
);
180 if (n
< 0) num_bytes
= 0;
183 file_buf
[num_bytes
] = '\0';
187 model
= models
+ sizeof models
/ sizeof models
[0];
188 for (p
= file_buf
; *p
; ++p
) {
189 /* Beginning of line */
190 if (strncmp(p
, "processor", sizeof "processor" - 1 ) != 0) continue;
192 m
= strstr(p
, "machine");
193 if (m
== NULL
) continue;
195 p
= m
+ sizeof "machine" - 1;
196 while (isspace(*p
) || *p
== '=') {
197 if (*p
== '\n') goto next_line
;
202 for (n
= 0; n
< sizeof models
/ sizeof models
[0]; ++n
) {
203 model_info
*mm
= models
+ n
;
204 size_t len
= strlen(mm
->cpuinfo_name
);
205 if (strncmp(mm
->cpuinfo_name
, model_name
, len
) == 0 &&
206 isspace(model_name
[len
])) {
207 /* In case there are different CPUs in this cluster return the
208 one with the dewest capabilities ("oldest" model). */
209 if (mm
< model
) model
= mm
;
210 p
= model_name
+ len
;
214 /* Skip until end-of-line */
222 if (model
== models
+ sizeof models
/ sizeof models
[0]) return NULL
;
228 /* Convenience macro that maps the facility bit number as given in the
229 Principles of Ops "facility indications" section to a bit mask */
230 #define FAC_BIT(x) (1ULL << (63 - (x)))
232 static int go(char *feature
, char *cpu
)
234 unsigned long long facilities
[S390_NUM_FACILITY_DW
];
235 unsigned long long match
;
236 model_info
*host
, *from
, *to
, *p
;
239 clear_facilities(facilities
);
242 if (strcmp(feature
, "s390x-zarch") == 0 ) {
243 match
= (facilities
[0] & FAC_BIT(1)) && (facilities
[0] & FAC_BIT(2));
244 } else if (strcmp(feature
, "s390x-n3") == 0 ) {
245 match
= facilities
[0] & FAC_BIT(0);
246 } else if (strcmp(feature
, "s390x-stfle") == 0 ) {
247 match
= facilities
[0] & FAC_BIT(7);
248 } else if (strcmp(feature
, "s390x-ldisp") == 0 ) {
249 match
= (facilities
[0] & FAC_BIT(18)) && (facilities
[0] & FAC_BIT(19));
250 } else if (strcmp(feature
, "s390x-eimm") == 0 ) {
251 match
= facilities
[0] & FAC_BIT(21);
252 } else if (strcmp(feature
, "s390x-stckf") == 0 ) {
253 match
= facilities
[0] & FAC_BIT(25);
254 } else if (strcmp(feature
, "s390x-genins") == 0 ) {
255 match
= facilities
[0] & FAC_BIT(34);
256 } else if (strcmp(feature
, "s390x-exrl") == 0 ) {
257 match
= facilities
[0] & FAC_BIT(35);
258 } else if (strcmp(feature
, "s390x-etf3") == 0 ) {
259 match
= facilities
[0] & FAC_BIT(30);
260 } else if (strcmp(feature
, "s390x-fpext") == 0 ) {
261 match
= facilities
[0] & FAC_BIT(37);
262 } else if (strcmp(feature
, "s390x-dfp") == 0 ) {
263 match
= facilities
[0] & FAC_BIT(42);
264 } else if (strcmp(feature
, "s390x-pfpo") == 0 ) {
265 match
= facilities
[0] & FAC_BIT(44);
266 } else if (strcmp(feature
, "s390x-highw") == 0 ) {
267 match
= facilities
[0] & FAC_BIT(45);
268 } else if (strcmp(feature
, "s390x-vx") == 0 ) {
269 /* VX needs kernel support; thus check the appropriate HWCAP bit. */
270 match
= GET_HWCAP() & 0x800;
271 } else if (strcmp(feature
, "s390x-msa5") == 0 ) {
272 match
= facilities
[0] & FAC_BIT(57); /* message security assist 5 facility */
273 } else if (strcmp(feature
, "s390x-mi2") == 0 ) {
274 match
= facilities
[0] & FAC_BIT(58);
275 } else if (strcmp(feature
, "s390x-mi3") == 0 ) {
276 match
= facilities
[0] & FAC_BIT(61);
277 } else if (strcmp(feature
, "s390x-vx2") == 0 ) {
278 match
= (GET_HWCAP() & 0x800) && (facilities
[2] & FAC_BIT(20));
280 return 2; // Unrecognised feature.
283 if (match
== 0) return 1; // facility not provided
285 /* Host provides facility. If no CPU was specified, we're done. */
286 if (cpu
== NULL
) return 0;
289 if (host
== NULL
) return 1; // unknown model
291 // printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name);
293 /* Determine interval of models in which to search for HOST. */
295 colon
= strchr(cpu
, ':');
299 from
= to
= locate_model(cpu
);
300 } else if (colon
== cpu
) {
301 // :NAME match machines up to and including CPU
303 to
= locate_model(cpu
+ 1);
304 } else if (colon
[1] == '\0') {
305 // NAME: match machines beginning with CPU or later
307 from
= locate_model(cpu
);
308 to
= models
+ sizeof models
/ sizeof models
[0] - 1;
311 // NAME:NAME match machines in interval
313 from
= locate_model(cpu
);
314 to
= locate_model(colon
+ 1);
318 if (from
== NULL
|| to
== NULL
|| from
> to
) {
319 fprintf(stderr
, "invalid cpu specification '%s'\n", cpu
);
324 printf("from %s (%s) to %s (%s)\n", from
->cpuinfo_name
, from
->real_name
,
325 to
->cpuinfo_name
, to
->real_name
);
328 /* Search for HOST. */
329 for (p
= from
; p
<= to
; ++p
) {
330 if (p
== host
) return 0;
333 return 1; // host does not match CPU specification
338 static int go(char *feature
, char *cpu
)
340 return 2; // Feature not recognised (non-s390x machine!)
346 //---------------------------------------------------------------------------
348 //---------------------------------------------------------------------------
349 int main(int argc
, char **argv
)
351 int rc
, inverted
= 0;
353 if (argc
< 2 || argc
> 3) {
354 fprintf( stderr
, "usage: s390x_features <feature> [<machine-model>]\n" );
355 exit(3); // Usage error.
358 if (argv
[1][0] == '!') {
359 assert(argv
[2] == NULL
); // not allowed
364 rc
= go(argv
[1], argv
[2]);
368 case 0: rc
= 1; break;
369 case 1: rc
= 0; break;
370 case 2: rc
= 2; break;
374 // printf("rc = %d\n", rc);