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
13 #include <features.h> // __GLIBC_PREREQ
15 // This file determines s390x features a processor supports.
18 // - 0 if the machine provides the asked-for feature and the cpu
19 // model, if specified, matches the machine
20 // - 1 the machine does not provide the asked-for feature or the
21 // cpu model, if specified, does not match the machine
22 // - 1 for an unknown cpu model in /proc/cpu_info
23 // - 2 if the asked-for feature isn't recognised (this will be the case for
24 // any feature if run on a non-s390x machine).
25 // - 3 if there was a usage error (it also prints an error message).
29 // s390x_features <feature> [<machine-model>]
31 // The machine_model is optional and it can be something like:
33 // z9 -- Host needs to be a z9 (and nothing else)
34 // z9: -- Host needs to be a z9 or any later model
35 // :z9 -- Host needs to be a model up to and including z9
36 // z900:z9 -- Host needs to be at least a z900 and at most a z9.
37 // Any model in between is OK, too.
41 #if defined(VGA_s390x)
43 // Features that require kernel support should be checked against HWCAP instead
44 // of the CPU facility list. To read the HWCAP, use 'getauxval' if available --
45 // which should be the case with glibc versions >= 2.16. A system with an older
46 // glibc is unlikely to support any of these features anyhow.
47 #if __GLIBC_PREREQ(2, 16)
49 #define GET_HWCAP() getauxval(AT_HWCAP)
51 #define GET_HWCAP() 0UL
54 /* Number of double words needed to store all facility bits. */
55 #define S390_NUM_FACILITY_DW 3
57 void handle_sigill(int signum
)
62 static void clear_facilities(unsigned long long *ret
)
65 for(index
= 0; index
< S390_NUM_FACILITY_DW
; index
++)
71 void stfle(unsigned long long *ret
)
73 signal(SIGILL
, handle_sigill
);
75 /* stfle not available: assume no facilities */
76 clear_facilities(ret
);
78 register unsigned long long r0
asm("0") = S390_NUM_FACILITY_DW
- 1;
79 asm volatile(".insn s,0xb2b00000,%0\n" /* stfle */
80 : "=m" (*ret
), "+d"(r0
) :: "cc", "memory");
85 /* Read /proc/cpuinfo. Look for lines like these
87 processor 0: version = FF, identification = 0117C9, machine = 2064
89 and return the machine model or NULL on error.
90 Adapted from function VG_(get_machine_model) in coregrind/m_machine.c */
93 const char *cpuinfo_name
;
94 const char *real_name
;
97 /* Array needs to be sorted chronologically. Oldest to newest */
98 model_info models
[] = {
105 { "2097", "z10-EC" },
106 { "2098", "z10-BC" },
114 { "3907", "z14 ZR1"},
118 /* Locate a machine model by name. Name can be either the cpuinfo
119 name or the external name. */
120 static model_info
*locate_model(const char *name
)
124 /* Try cpuinfo name first */
125 for (p
= models
; p
!= models
+ sizeof models
/ sizeof models
[0]; ++p
) {
126 if (strcmp(p
->cpuinfo_name
, name
) == 0) return p
; // found it
129 /* Now try external name */
130 for (p
= models
; p
!= models
+ sizeof models
/ sizeof models
[0]; ++p
) {
131 if (strcmp(p
->real_name
, name
) == 0) return p
; // found it
138 static model_info
*get_host(void)
141 size_t num_bytes
, file_buf_size
;
142 char *p
, *m
, *model_name
, *file_buf
;
145 /* Slurp contents of /proc/cpuinfo into FILE_BUF */
146 fh
= open("/proc/cpuinfo", O_RDONLY
, S_IRUSR
);
147 if (fh
< 0) return NULL
;
149 /* Determine the size of /proc/cpuinfo.
150 Work around broken-ness in /proc file system implementation.
151 fstat returns a zero size for /proc/cpuinfo although it is
152 claimed to be a regular file. */
154 file_buf_size
= 1000;
155 file_buf
= malloc(file_buf_size
+ 1);
158 n
= read(fh
, file_buf
, file_buf_size
);
162 if (n
< file_buf_size
) break; /* reached EOF */
165 if (n
< 0) num_bytes
= 0; /* read error; ignore contents */
167 if (num_bytes
> file_buf_size
) {
169 lseek(fh
, 0, SEEK_SET
);
170 file_buf
= malloc(num_bytes
+ 1);
171 n
= read(fh
, file_buf
, num_bytes
);
172 if (n
< 0) num_bytes
= 0;
175 file_buf
[num_bytes
] = '\0';
179 model
= models
+ sizeof models
/ sizeof models
[0];
180 for (p
= file_buf
; *p
; ++p
) {
181 /* Beginning of line */
182 if (strncmp(p
, "processor", sizeof "processor" - 1 ) != 0) continue;
184 m
= strstr(p
, "machine");
185 if (m
== NULL
) continue;
187 p
= m
+ sizeof "machine" - 1;
188 while (isspace(*p
) || *p
== '=') {
189 if (*p
== '\n') goto next_line
;
194 for (n
= 0; n
< sizeof models
/ sizeof models
[0]; ++n
) {
195 model_info
*mm
= models
+ n
;
196 size_t len
= strlen(mm
->cpuinfo_name
);
197 if (strncmp(mm
->cpuinfo_name
, model_name
, len
) == 0 &&
198 isspace(model_name
[len
])) {
199 /* In case there are different CPUs in this cluster return the
200 one with the dewest capabilities ("oldest" model). */
201 if (mm
< model
) model
= mm
;
202 p
= model_name
+ len
;
206 /* Skip until end-of-line */
214 if (model
== models
+ sizeof models
/ sizeof models
[0]) return NULL
;
220 /* Convenience macro that maps the facility bit number as given in the
221 Principles of Ops "facility indications" section to a bit mask */
222 #define FAC_BIT(x) (1ULL << (63 - (x)))
224 static int go(char *feature
, char *cpu
)
226 unsigned long long facilities
[S390_NUM_FACILITY_DW
];
227 unsigned long long match
;
228 model_info
*host
, *from
, *to
, *p
;
231 clear_facilities(facilities
);
234 if (strcmp(feature
, "s390x-zarch") == 0 ) {
235 match
= (facilities
[0] & FAC_BIT(1)) && (facilities
[0] & FAC_BIT(2));
236 } else if (strcmp(feature
, "s390x-n3") == 0 ) {
237 match
= facilities
[0] & FAC_BIT(0);
238 } else if (strcmp(feature
, "s390x-stfle") == 0 ) {
239 match
= facilities
[0] & FAC_BIT(7);
240 } else if (strcmp(feature
, "s390x-ldisp") == 0 ) {
241 match
= (facilities
[0] & FAC_BIT(18)) && (facilities
[0] & FAC_BIT(19));
242 } else if (strcmp(feature
, "s390x-eimm") == 0 ) {
243 match
= facilities
[0] & FAC_BIT(21);
244 } else if (strcmp(feature
, "s390x-stckf") == 0 ) {
245 match
= facilities
[0] & FAC_BIT(25);
246 } else if (strcmp(feature
, "s390x-genins") == 0 ) {
247 match
= facilities
[0] & FAC_BIT(34);
248 } else if (strcmp(feature
, "s390x-exrl") == 0 ) {
249 match
= facilities
[0] & FAC_BIT(35);
250 } else if (strcmp(feature
, "s390x-etf3") == 0 ) {
251 match
= facilities
[0] & FAC_BIT(30);
252 } else if (strcmp(feature
, "s390x-fpext") == 0 ) {
253 match
= facilities
[0] & FAC_BIT(37);
254 } else if (strcmp(feature
, "s390x-dfp") == 0 ) {
255 match
= facilities
[0] & FAC_BIT(42);
256 } else if (strcmp(feature
, "s390x-pfpo") == 0 ) {
257 match
= facilities
[0] & FAC_BIT(44);
258 } else if (strcmp(feature
, "s390x-highw") == 0 ) {
259 match
= facilities
[0] & FAC_BIT(45);
260 } else if (strcmp(feature
, "s390x-vx") == 0 ) {
261 /* VX needs kernel support; thus check the appropriate HWCAP bit. */
262 match
= GET_HWCAP() & 0x800;
263 } else if (strcmp(feature
, "s390x-msa5") == 0 ) {
264 match
= facilities
[0] & FAC_BIT(57); /* message security assist 5 facility */
265 } else if (strcmp(feature
, "s390x-mi2") == 0 ) {
266 match
= facilities
[0] & FAC_BIT(58);
268 return 2; // Unrecognised feature.
271 if (match
== 0) return 1; // facility not provided
273 /* Host provides facility. If no CPU was specified, we're done. */
274 if (cpu
== NULL
) return 0;
277 if (host
== NULL
) return 1; // unknown model
279 // printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name);
281 /* Determine interval of models in which to search for HOST. */
283 colon
= strchr(cpu
, ':');
287 from
= to
= locate_model(cpu
);
288 } else if (colon
== cpu
) {
289 // :NAME match machines up to and including CPU
291 to
= locate_model(cpu
+ 1);
292 } else if (colon
[1] == '\0') {
293 // NAME: match machines beginning with CPU or later
295 from
= locate_model(cpu
);
296 to
= models
+ sizeof models
/ sizeof models
[0] - 1;
299 // NAME:NAME match machines in interval
301 from
= locate_model(cpu
);
302 to
= locate_model(colon
+ 1);
306 if (from
== NULL
|| to
== NULL
|| from
> to
) {
307 fprintf(stderr
, "invalid cpu specification '%s'\n", cpu
);
312 printf("from %s (%s) to %s (%s)\n", from
->cpuinfo_name
, from
->real_name
,
313 to
->cpuinfo_name
, to
->real_name
);
316 /* Search for HOST. */
317 for (p
= from
; p
<= to
; ++p
) {
318 if (p
== host
) return 0;
321 return 1; // host does not match CPU specification
326 static int go(char *feature
, char *cpu
)
328 return 2; // Feature not recognised (non-s390x machine!)
334 //---------------------------------------------------------------------------
336 //---------------------------------------------------------------------------
337 int main(int argc
, char **argv
)
339 int rc
, inverted
= 0;
341 if (argc
< 2 || argc
> 3) {
342 fprintf( stderr
, "usage: s390x_features <feature> [<machine-model>]\n" );
343 exit(3); // Usage error.
346 if (argv
[1][0] == '!') {
347 assert(argv
[2] == NULL
); // not allowed
352 rc
= go(argv
[1], argv
[2]);
356 case 0: rc
= 1; break;
357 case 1: rc
= 0; break;
358 case 2: rc
= 2; break;
362 // printf("rc = %d\n", rc);