1 /* -*- mode: C; c-basic-offset: 3; -*- */ 2 3 #include <setjmp.h> 4 #include <signal.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <assert.h> 9 #include <ctype.h> // isspace 10 #include <fcntl.h> // open 11 #include <unistd.h> // lseek 12 #include <sys/stat.h> // S_IRUSR 13 14 // This file determines s390x features a processor supports. 15 // 16 // We return: 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). 25 // 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. 37 38 jmp_buf env; 39 40 #if defined(VGA_s390x) 41 42 void handle_sigill(int signum) 43 { 44 longjmp(env, 1); 45 } 46 47 unsigned 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 64 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 72 typedef 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 */ 78 model_info models[] = { 79 { "2064", "z900" }, 80 { "2066", "z800" }, 81 { "2084", "z990" }, 82 { "2086", "z890" }, 83 { "2094", "z9-EC" }, 84 { "2096", "z9-BC" }, 85 { "2097", "z10-EC" }, 86 { "2098", "z10-BC" }, 87 { "2817", "z196" }, 88 { "2818", "z114" }, 89 { "2827", "zEC12" }, 90 { "2828", "zBC12" }, 91 { "2964", "z13" }, 92 { "2965", "z13s" }, 93 }; 94 95 96 /* Locate a machine model by name. Name can be either the cpuinfo 97 name or the external name. */ 98 static model_info *locate_model(const char *name) 99 { 100 model_info *p; 101 102 /* Try cpuinfo name first */ 103 for (p = models; p != models + sizeof models / sizeof models[0]; ++p) { 104 if (strcmp(p->cpuinfo_name, name) == 0) return p; // found it 105 } 106 107 /* Now try external name */ 108 for (p = models; p != models + sizeof models / sizeof models[0]; ++p) { 109 if (strcmp(p->real_name, name) == 0) return p; // found it 110 } 111 112 return NULL; 113 } 114 115 116 static model_info *get_host(void) 117 { 118 int n, fh; 119 size_t num_bytes, file_buf_size; 120 char *p, *m, *model_name, *file_buf; 121 model_info *model; 122 123 /* Slurp contents of /proc/cpuinfo into FILE_BUF */ 124 fh = open("/proc/cpuinfo", O_RDONLY, S_IRUSR); 125 if (fh < 0) return NULL; 126 127 /* Determine the size of /proc/cpuinfo. 128 Work around broken-ness in /proc file system implementation. 129 fstat returns a zero size for /proc/cpuinfo although it is 130 claimed to be a regular file. */ 131 num_bytes = 0; 132 file_buf_size = 1000; 133 file_buf = malloc(file_buf_size + 1); 134 135 while (42) { 136 n = read(fh, file_buf, file_buf_size); 137 if (n < 0) break; 138 139 num_bytes += n; 140 if (n < file_buf_size) break; /* reached EOF */ 141 } 142 143 if (n < 0) num_bytes = 0; /* read error; ignore contents */ 144 145 if (num_bytes > file_buf_size) { 146 free(file_buf); 147 lseek(fh, 0, SEEK_SET); 148 file_buf = malloc(num_bytes + 1); 149 n = read(fh, file_buf, num_bytes); 150 if (n < 0) num_bytes = 0; 151 } 152 153 file_buf[num_bytes] = '\0'; 154 close(fh); 155 156 /* Parse file */ 157 model = models + sizeof models / sizeof models[0]; 158 for (p = file_buf; *p; ++p) { 159 /* Beginning of line */ 160 if (strncmp(p, "processor", sizeof "processor" - 1 ) != 0) continue; 161 162 m = strstr(p, "machine"); 163 if (m == NULL) continue; 164 165 p = m + sizeof "machine" - 1; 166 while (isspace(*p) || *p == '=') { 167 if (*p == '\n') goto next_line; 168 ++p; 169 } 170 171 model_name = p; 172 for (n = 0; n < sizeof models / sizeof models[0]; ++n) { 173 model_info *mm = models + n; 174 size_t len = strlen(mm->cpuinfo_name); 175 if (strncmp(mm->cpuinfo_name, model_name, len) == 0 && 176 isspace(model_name[len])) { 177 /* In case there are different CPUs in this cluster return the 178 one with the dewest capabilities ("oldest" model). */ 179 if (mm < model) model = mm; 180 p = model_name + len; 181 break; 182 } 183 } 184 /* Skip until end-of-line */ 185 while (*p != '\n') 186 ++p; 187 next_line: ; 188 } 189 190 free(file_buf); 191 192 if (model == models + sizeof models / sizeof models[0]) return NULL; 193 194 return model; 195 } 196 197 198 /* Convenience macro that maps the facility bit number as given in the 199 Principles of Ops "facility indications" section to a bit mask */ 200 #define FAC_BIT(x) (1ULL << (63 - (x))) 201 202 static int go(char *feature, char *cpu) 203 { 204 unsigned long long facilities; 205 unsigned long long match; 206 model_info *host, *from, *to, *p; 207 char *colon; 208 209 facilities = stfle(); 210 211 if (strcmp(feature, "s390x-zarch") == 0 ) { 212 match = (facilities & FAC_BIT(1)) && (facilities & FAC_BIT(2)); 213 } else if (strcmp(feature, "s390x-n3") == 0 ) { 214 match = facilities & FAC_BIT(0); 215 } else if (strcmp(feature, "s390x-stfle") == 0 ) { 216 match = facilities & FAC_BIT(7); 217 } else if (strcmp(feature, "s390x-ldisp") == 0 ) { 218 match = (facilities & FAC_BIT(18)) && (facilities & FAC_BIT(19)); 219 } else if (strcmp(feature, "s390x-eimm") == 0 ) { 220 match = facilities & FAC_BIT(21); 221 } else if (strcmp(feature, "s390x-stckf") == 0 ) { 222 match = facilities & FAC_BIT(25); 223 } else if (strcmp(feature, "s390x-genins") == 0 ) { 224 match = facilities & FAC_BIT(34); 225 } else if (strcmp(feature, "s390x-exrl") == 0 ) { 226 match = facilities & FAC_BIT(35); 227 } else if (strcmp(feature, "s390x-etf3") == 0 ) { 228 match = facilities & FAC_BIT(30); 229 } else if (strcmp(feature, "s390x-fpext") == 0 ) { 230 match = facilities & FAC_BIT(37); 231 } else if (strcmp(feature, "s390x-dfp") == 0 ) { 232 match = facilities & FAC_BIT(42); 233 } else if (strcmp(feature, "s390x-pfpo") == 0 ) { 234 match = facilities & FAC_BIT(44); 235 } else if (strcmp(feature, "s390x-highw") == 0 ) { 236 match = facilities & FAC_BIT(45); 237 } else { 238 return 2; // Unrecognised feature. 239 } 240 241 if (match == 0) return 1; // facility not provided 242 243 /* Host provides facility. If no CPU was specified, we're done. */ 244 if (cpu == NULL) return 0; 245 246 host = get_host(); 247 if (host == NULL) return 1; // unknown model 248 249 // printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name); 250 251 /* Determine interval of models in which to search for HOST. */ 252 from = to = NULL; 253 colon = strchr(cpu, ':'); 254 255 if (colon == NULL) { 256 // match exact 257 from = to = locate_model(cpu); 258 } else if (colon == cpu) { 259 // :NAME match machines up to and including CPU 260 from = models; 261 to = locate_model(cpu + 1); 262 } else if (colon[1] == '\0') { 263 // NAME: match machines beginning with CPU or later 264 *colon = '\0'; 265 from = locate_model(cpu); 266 to = models + sizeof models / sizeof models[0] - 1; 267 *colon = ':'; 268 } else { 269 // NAME:NAME match machines in interval 270 *colon = '\0'; 271 from = locate_model(cpu); 272 to = locate_model(colon + 1); 273 *colon = ':'; 274 } 275 276 if (from == NULL || to == NULL || from > to) { 277 fprintf(stderr, "invalid cpu specification '%s'\n", cpu); 278 return 3; 279 } 280 281 #if 0 282 printf("from %s (%s) to %s (%s)\n", from->cpuinfo_name, from->real_name, 283 to->cpuinfo_name, to->real_name); 284 #endif 285 286 /* Search for HOST. */ 287 for (p = from; p <= to; ++p) { 288 if (p == host) return 0; 289 } 290 291 return 1; // host does not match CPU specification 292 } 293 294 #else 295 296 static int go(char *feature, char *cpu) 297 { 298 return 2; // Feature not recognised (non-s390x machine!) 299 } 300 301 #endif 302 303 304 //--------------------------------------------------------------------------- 305 // main 306 //--------------------------------------------------------------------------- 307 int main(int argc, char **argv) 308 { 309 int rc, inverted = 0; 310 311 if (argc < 2 || argc > 3) { 312 fprintf( stderr, "usage: s390x_features <feature> [<machine-model>]\n" ); 313 exit(3); // Usage error. 314 } 315 316 if (argv[1][0] == '!') { 317 assert(argv[2] == NULL); // not allowed 318 inverted = 1; 319 ++argv[1]; 320 } 321 322 rc = go(argv[1], argv[2]); 323 324 if (inverted) { 325 switch (rc) { 326 case 0: rc = 1; break; 327 case 1: rc = 0; break; 328 case 2: rc = 2; break; 329 } 330 } 331 332 // printf("rc = %d\n", rc); 333 334 return rc; 335 } 336