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 // This file determines s390x features a processor supports.
17 // - 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
21 // - 1 for an unknown cpu model in /proc/cpu_info
22 // - 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).
28 // s390x_features <feature> [<machine-model>]
30 // The machine_model is optional and it can be something like:
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.
40 #if defined(VGA_s390x)
42 void handle_sigill(int signum
)
47 unsigned long long stfle(void)
50 unsigned long long ret
;
52 signal(SIGILL
, handle_sigill
);
54 /* stfle not available: assume no facilities */
57 asm volatile("lghi 0, 0\n"
58 ".insn s,0xb2b00000,%0\n" /* stfle */
59 : "=Q" (ret
)::"0", "cc");
65 /* Read /proc/cpuinfo. Look for lines like these
67 processor 0: version = FF, identification = 0117C9, machine = 2064
69 and return the machine model or NULL on error.
70 Adapted from function VG_(get_machine_model) in coregrind/m_machine.c */
73 const char *cpuinfo_name
;
74 const char *real_name
;
77 /* Array needs to be sorted chronologically. Oldest to newest */
78 model_info models
[] = {
94 /* Locate a machine model by name. Name can be either the cpuinfo
95 name or the external name. */
96 static model_info
*locate_model(const char *name
)
100 /* Try cpuinfo name first */
101 for (p
= models
; p
!= models
+ sizeof models
/ sizeof models
[0]; ++p
) {
102 if (strcmp(p
->cpuinfo_name
, name
) == 0) return p
; // found it
105 /* Now try external name */
106 for (p
= models
; p
!= models
+ sizeof models
/ sizeof models
[0]; ++p
) {
107 if (strcmp(p
->real_name
, name
) == 0) return p
; // found it
114 static model_info
*get_host(void)
117 size_t num_bytes
, file_buf_size
;
118 char *p
, *m
, *model_name
, *file_buf
;
121 /* Slurp contents of /proc/cpuinfo into FILE_BUF */
122 fh
= open("/proc/cpuinfo", O_RDONLY
, S_IRUSR
);
123 if (fh
< 0) return NULL
;
125 /* Determine the size of /proc/cpuinfo.
126 Work around broken-ness in /proc file system implementation.
127 fstat returns a zero size for /proc/cpuinfo although it is
128 claimed to be a regular file. */
130 file_buf_size
= 1000;
131 file_buf
= malloc(file_buf_size
+ 1);
134 n
= read(fh
, file_buf
, file_buf_size
);
138 if (n
< file_buf_size
) break; /* reached EOF */
141 if (n
< 0) num_bytes
= 0; /* read error; ignore contents */
143 if (num_bytes
> file_buf_size
) {
145 lseek(fh
, 0, SEEK_SET
);
146 file_buf
= malloc(num_bytes
+ 1);
147 n
= read(fh
, file_buf
, num_bytes
);
148 if (n
< 0) num_bytes
= 0;
151 file_buf
[num_bytes
] = '\0';
155 model
= models
+ sizeof models
/ sizeof models
[0];
156 for (p
= file_buf
; *p
; ++p
) {
157 /* Beginning of line */
158 if (strncmp(p
, "processor", sizeof "processor" - 1 ) != 0) continue;
160 m
= strstr(p
, "machine");
161 if (m
== NULL
) continue;
163 p
= m
+ sizeof "machine" - 1;
164 while (isspace(*p
) || *p
== '=') {
165 if (*p
== '\n') goto next_line
;
170 for (n
= 0; n
< sizeof models
/ sizeof models
[0]; ++n
) {
171 model_info
*mm
= models
+ n
;
172 size_t len
= strlen(mm
->cpuinfo_name
);
173 if (strncmp(mm
->cpuinfo_name
, model_name
, len
) == 0 &&
174 isspace(model_name
[len
])) {
175 /* In case there are different CPUs in this cluster return the
176 one with the dewest capabilities ("oldest" model). */
177 if (mm
< model
) model
= mm
;
178 p
= model_name
+ len
;
182 /* Skip until end-of-line */
190 if (model
== models
+ sizeof models
/ sizeof models
[0]) return NULL
;
196 /* Convenience macro that maps the facility bit number as given in the
197 Principles of Ops "facility indications" section to a bit mask */
198 #define FAC_BIT(x) (1ULL << (63 - (x)))
200 static int go(char *feature
, char *cpu
)
202 unsigned long long facilities
;
203 unsigned long long match
;
204 model_info
*host
, *from
, *to
, *p
;
207 facilities
= stfle();
209 if (strcmp(feature
, "s390x-zarch") == 0 ) {
210 match
= (facilities
& FAC_BIT(1)) && (facilities
& FAC_BIT(2));
211 } else if (strcmp(feature
, "s390x-n3") == 0 ) {
212 match
= facilities
& FAC_BIT(0);
213 } else if (strcmp(feature
, "s390x-stfle") == 0 ) {
214 match
= facilities
& FAC_BIT(7);
215 } else if (strcmp(feature
, "s390x-ldisp") == 0 ) {
216 match
= (facilities
& FAC_BIT(18)) && (facilities
& FAC_BIT(19));
217 } else if (strcmp(feature
, "s390x-eimm") == 0 ) {
218 match
= facilities
& FAC_BIT(21);
219 } else if (strcmp(feature
, "s390x-stckf") == 0 ) {
220 match
= facilities
& FAC_BIT(25);
221 } else if (strcmp(feature
, "s390x-genins") == 0 ) {
222 match
= facilities
& FAC_BIT(34);
223 } else if (strcmp(feature
, "s390x-exrl") == 0 ) {
224 match
= facilities
& FAC_BIT(35);
225 } else if (strcmp(feature
, "s390x-etf3") == 0 ) {
226 match
= facilities
& FAC_BIT(30);
227 } else if (strcmp(feature
, "s390x-fpext") == 0 ) {
228 match
= facilities
& FAC_BIT(37);
229 } else if (strcmp(feature
, "s390x-dfp") == 0 ) {
230 match
= facilities
& FAC_BIT(42);
231 } else if (strcmp(feature
, "s390x-pfpo") == 0 ) {
232 match
= facilities
& FAC_BIT(44);
234 return 2; // Unrecognised feature.
237 if (match
== 0) return 1; // facility not provided
239 /* Host provides facility. If no CPU was specified, we're done. */
240 if (cpu
== NULL
) return 0;
243 if (host
== NULL
) return 1; // unknown model
245 // printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name);
247 /* Determine interval of models in which to search for HOST. */
249 colon
= strchr(cpu
, ':');
253 from
= to
= locate_model(cpu
);
254 } else if (colon
== cpu
) {
255 // :NAME match machines up to and including CPU
257 to
= locate_model(cpu
+ 1);
258 } else if (colon
[1] == '\0') {
259 // NAME: match machines beginning with CPU or later
261 from
= locate_model(cpu
);
262 to
= models
+ sizeof models
/ sizeof models
[0] - 1;
265 // NAME:NAME match machines in interval
267 from
= locate_model(cpu
);
268 to
= locate_model(colon
+ 1);
272 if (from
== NULL
|| to
== NULL
|| from
> to
) {
273 fprintf(stderr
, "invalid cpu specification '%s'\n", cpu
);
278 printf("from %s (%s) to %s (%s)\n", from
->cpuinfo_name
, from
->real_name
,
279 to
->cpuinfo_name
, to
->real_name
);
282 /* Search for HOST. */
283 for (p
= from
; p
<= to
; ++p
) {
284 if (p
== host
) return 0;
287 return 1; // host does not match CPU specification
292 static int go(char *feature
, char *cpu
)
294 return 2; // Feature not recognised (non-s390x machine!)
300 //---------------------------------------------------------------------------
302 //---------------------------------------------------------------------------
303 int main(int argc
, char **argv
)
305 int rc
, inverted
= 0;
307 if (argc
< 2 || argc
> 3) {
308 fprintf( stderr
, "usage: s390x_features <feature> [<machine-model>]\n" );
309 exit(3); // Usage error.
312 if (argv
[1][0] == '!') {
313 assert(argv
[2] == NULL
); // not allowed
318 rc
= go(argv
[1], argv
[2]);
322 case 0: rc
= 1; break;
323 case 1: rc
= 0; break;
324 case 2: rc
= 2; break;
328 // printf("rc = %d\n", rc);