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"},
124 /* Locate a machine model by name. Name can be either the cpuinfo
125 name or the external name. */
126 static model_info
*locate_model(const char *name
)
130 /* Try cpuinfo name first */
131 for (p
= models
; p
!= models
+ sizeof models
/ sizeof models
[0]; ++p
) {
132 if (strcmp(p
->cpuinfo_name
, name
) == 0) return p
; // found it
135 /* Now try external name */
136 for (p
= models
; p
!= models
+ sizeof models
/ sizeof models
[0]; ++p
) {
137 if (strcmp(p
->real_name
, name
) == 0) return p
; // found it
144 static model_info
*get_host(void)
147 size_t num_bytes
, file_buf_size
;
148 char *p
, *m
, *model_name
, *file_buf
;
151 /* Slurp contents of /proc/cpuinfo into FILE_BUF */
152 fh
= open("/proc/cpuinfo", O_RDONLY
, S_IRUSR
);
153 if (fh
< 0) return NULL
;
155 /* Determine the size of /proc/cpuinfo.
156 Work around broken-ness in /proc file system implementation.
157 fstat returns a zero size for /proc/cpuinfo although it is
158 claimed to be a regular file. */
160 file_buf_size
= 1000;
161 file_buf
= malloc(file_buf_size
+ 1);
164 n
= read(fh
, file_buf
, file_buf_size
);
168 if (n
< file_buf_size
) break; /* reached EOF */
171 if (n
< 0) num_bytes
= 0; /* read error; ignore contents */
173 if (num_bytes
> file_buf_size
) {
175 lseek(fh
, 0, SEEK_SET
);
176 file_buf
= malloc(num_bytes
+ 1);
177 n
= read(fh
, file_buf
, num_bytes
);
178 if (n
< 0) num_bytes
= 0;
181 file_buf
[num_bytes
] = '\0';
185 model
= models
+ sizeof models
/ sizeof models
[0];
186 for (p
= file_buf
; *p
; ++p
) {
187 /* Beginning of line */
188 if (strncmp(p
, "processor", sizeof "processor" - 1 ) != 0) continue;
190 m
= strstr(p
, "machine");
191 if (m
== NULL
) continue;
193 p
= m
+ sizeof "machine" - 1;
194 while (isspace(*p
) || *p
== '=') {
195 if (*p
== '\n') goto next_line
;
200 for (n
= 0; n
< sizeof models
/ sizeof models
[0]; ++n
) {
201 model_info
*mm
= models
+ n
;
202 size_t len
= strlen(mm
->cpuinfo_name
);
203 if (strncmp(mm
->cpuinfo_name
, model_name
, len
) == 0 &&
204 isspace(model_name
[len
])) {
205 /* In case there are different CPUs in this cluster return the
206 one with the dewest capabilities ("oldest" model). */
207 if (mm
< model
) model
= mm
;
208 p
= model_name
+ len
;
212 /* Skip until end-of-line */
220 if (model
== models
+ sizeof models
/ sizeof models
[0]) return NULL
;
226 /* Convenience macro that maps the facility bit number as given in the
227 Principles of Ops "facility indications" section to a bit mask */
228 #define FAC_BIT(x) (1ULL << (63 - (x)))
230 static int go(char *feature
, char *cpu
)
232 unsigned long long facilities
[S390_NUM_FACILITY_DW
];
233 unsigned long long match
;
234 model_info
*host
, *from
, *to
, *p
;
237 clear_facilities(facilities
);
240 if (strcmp(feature
, "s390x-zarch") == 0 ) {
241 match
= (facilities
[0] & FAC_BIT(1)) && (facilities
[0] & FAC_BIT(2));
242 } else if (strcmp(feature
, "s390x-n3") == 0 ) {
243 match
= facilities
[0] & FAC_BIT(0);
244 } else if (strcmp(feature
, "s390x-stfle") == 0 ) {
245 match
= facilities
[0] & FAC_BIT(7);
246 } else if (strcmp(feature
, "s390x-ldisp") == 0 ) {
247 match
= (facilities
[0] & FAC_BIT(18)) && (facilities
[0] & FAC_BIT(19));
248 } else if (strcmp(feature
, "s390x-eimm") == 0 ) {
249 match
= facilities
[0] & FAC_BIT(21);
250 } else if (strcmp(feature
, "s390x-stckf") == 0 ) {
251 match
= facilities
[0] & FAC_BIT(25);
252 } else if (strcmp(feature
, "s390x-genins") == 0 ) {
253 match
= facilities
[0] & FAC_BIT(34);
254 } else if (strcmp(feature
, "s390x-exrl") == 0 ) {
255 match
= facilities
[0] & FAC_BIT(35);
256 } else if (strcmp(feature
, "s390x-etf3") == 0 ) {
257 match
= facilities
[0] & FAC_BIT(30);
258 } else if (strcmp(feature
, "s390x-fpext") == 0 ) {
259 match
= facilities
[0] & FAC_BIT(37);
260 } else if (strcmp(feature
, "s390x-dfp") == 0 ) {
261 match
= facilities
[0] & FAC_BIT(42);
262 } else if (strcmp(feature
, "s390x-pfpo") == 0 ) {
263 match
= facilities
[0] & FAC_BIT(44);
264 } else if (strcmp(feature
, "s390x-highw") == 0 ) {
265 match
= facilities
[0] & FAC_BIT(45);
266 } else if (strcmp(feature
, "s390x-vx") == 0 ) {
267 /* VX needs kernel support; thus check the appropriate HWCAP bit. */
268 match
= GET_HWCAP() & 0x800;
269 } else if (strcmp(feature
, "s390x-msa5") == 0 ) {
270 match
= facilities
[0] & FAC_BIT(57); /* message security assist 5 facility */
271 } else if (strcmp(feature
, "s390x-mi2") == 0 ) {
272 match
= facilities
[0] & FAC_BIT(58);
274 return 2; // Unrecognised feature.
277 if (match
== 0) return 1; // facility not provided
279 /* Host provides facility. If no CPU was specified, we're done. */
280 if (cpu
== NULL
) return 0;
283 if (host
== NULL
) return 1; // unknown model
285 // printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name);
287 /* Determine interval of models in which to search for HOST. */
289 colon
= strchr(cpu
, ':');
293 from
= to
= locate_model(cpu
);
294 } else if (colon
== cpu
) {
295 // :NAME match machines up to and including CPU
297 to
= locate_model(cpu
+ 1);
298 } else if (colon
[1] == '\0') {
299 // NAME: match machines beginning with CPU or later
301 from
= locate_model(cpu
);
302 to
= models
+ sizeof models
/ sizeof models
[0] - 1;
305 // NAME:NAME match machines in interval
307 from
= locate_model(cpu
);
308 to
= locate_model(colon
+ 1);
312 if (from
== NULL
|| to
== NULL
|| from
> to
) {
313 fprintf(stderr
, "invalid cpu specification '%s'\n", cpu
);
318 printf("from %s (%s) to %s (%s)\n", from
->cpuinfo_name
, from
->real_name
,
319 to
->cpuinfo_name
, to
->real_name
);
322 /* Search for HOST. */
323 for (p
= from
; p
<= to
; ++p
) {
324 if (p
== host
) return 0;
327 return 1; // host does not match CPU specification
332 static int go(char *feature
, char *cpu
)
334 return 2; // Feature not recognised (non-s390x machine!)
340 //---------------------------------------------------------------------------
342 //---------------------------------------------------------------------------
343 int main(int argc
, char **argv
)
345 int rc
, inverted
= 0;
347 if (argc
< 2 || argc
> 3) {
348 fprintf( stderr
, "usage: s390x_features <feature> [<machine-model>]\n" );
349 exit(3); // Usage error.
352 if (argv
[1][0] == '!') {
353 assert(argv
[2] == NULL
); // not allowed
358 rc
= go(argv
[1], argv
[2]);
362 case 0: rc
= 1; break;
363 case 1: rc
= 0; break;
364 case 2: rc
= 2; break;
368 // printf("rc = %d\n", rc);