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 <ctype.h> // isspace 9 #include <fcntl.h> // open 10 #include <unistd.h> // lseek 11 #include <sys/stat.h> // S_IRUSR 12 13 // This file determines s390x features a processor supports. 14 // 15 // We return: 16 // - 0 if the machine provides the asked-for feature and the cpu 17 // model, if specified, matches the machine 18 // - 1 the machine does not provide the asked-for feature or the 19 // cpu model, if specified, does not match the machine 20 // - 2 if the asked-for feature isn't recognised (this will be the case for 21 // any feature if run on a non-s390x machine). 22 // - 2 for an unknown cpu model in /proc/cpu_info 23 // - 3 if there was a usage error (it also prints an error message). 24 // 25 // USAGE: 26 // 27 // s390x_features <feature> [<machine-model>] 28 // 29 // The machine_model is optional and it can be something like: 30 // 31 // z9 -- Host needs to be a z9 (and nothing else) 32 // z9: -- Host needs to be a z9 or any later model 33 // :z9 -- Host needs to be a model up to and including z9 34 // z900:z9 -- Host needs to be at least a z900 and at most a z9. 35 // Any model in between is OK, too. 36 37 jmp_buf env; 38 39 #if defined(VGA_s390x) 40 41 void handle_sigill(int signum) 42 { 43 longjmp(env, 1); 44 } 45 46 unsigned long long stfle(void) 47 { 48 49 unsigned long long ret; 50 51 signal(SIGILL, handle_sigill); 52 if (setjmp(env)) { 53 /* stfle not available: assume no facilities */ 54 return 0; 55 } else { 56 asm volatile("lghi 0, 0\n" 57 ".insn s,0xb2b00000,%0\n" /* stfle */ 58 : "=Q" (ret)::"0", "cc"); 59 return ret; 60 } 61 } 62 63 64 /* Read /proc/cpuinfo. Look for lines like these 65 66 processor 0: version = FF, identification = 0117C9, machine = 2064 67 68 and return the machine model or NULL on error. 69 Adapted from function VG_(get_machine_model) in coregrind/m_machine.c */ 70 71 typedef struct { 72 const char *cpuinfo_name; 73 const char *real_name; 74 } model_info; 75 76 /* Array needs to be sorted chronologically. Oldest to newest */ 77 model_info models[] = { 78 { "2064", "z900" }, 79 { "2066", "z800" }, 80 { "2084", "z990" }, 81 { "2086", "z890" }, 82 { "2094", "z9-ec" }, 83 { "2096", "z9-bc" }, 84 { "2097", "z10-ec" }, 85 { "2098", "z10-bc" }, 86 { "2817", "z196" }, 87 }; 88 89 90 /* Locate a machine model by name. Name can be either the cpuinfo 91 name or the external name. */ 92 static model_info *locate_model(const char *name) 93 { 94 model_info *p; 95 96 /* Try cpuinfo name first */ 97 for (p = models; p != models + sizeof models / sizeof models[0]; ++p) { 98 if (strcmp(p->cpuinfo_name, name) == 0) return p; // found it 99 } 100 101 /* Now try external name */ 102 for (p = models; p != models + sizeof models / sizeof models[0]; ++p) { 103 if (strcmp(p->real_name, name) == 0) return p; // found it 104 } 105 106 return NULL; 107 } 108 109 110 static model_info *get_host(void) 111 { 112 int n, fh; 113 size_t num_bytes, file_buf_size; 114 char *p, *m, *model_name, *file_buf; 115 model_info *model; 116 117 /* Slurp contents of /proc/cpuinfo into FILE_BUF */ 118 //fh = open("/proc/cpuinfo", O_RDONLY, S_IRUSR); 119 fh = open("/proc/cpuinfo", O_RDONLY, S_IRUSR); 120 if (fh < 0) return NULL; 121 122 /* Determine the size of /proc/cpuinfo. 123 Work around broken-ness in /proc file system implementation. 124 fstat returns a zero size for /proc/cpuinfo although it is 125 claimed to be a regular file. */ 126 num_bytes = 0; 127 file_buf_size = 1000; 128 file_buf = malloc(file_buf_size + 1); 129 130 while (42) { 131 n = read(fh, file_buf, file_buf_size); 132 if (n < 0) break; 133 134 num_bytes += n; 135 if (n < file_buf_size) break; /* reached EOF */ 136 } 137 138 if (n < 0) num_bytes = 0; /* read error; ignore contents */ 139 140 if (num_bytes > file_buf_size) { 141 free(file_buf); 142 lseek(fh, 0, SEEK_SET); 143 file_buf = malloc(num_bytes + 1); 144 n = read(fh, file_buf, num_bytes); 145 if (n < 0) num_bytes = 0; 146 } 147 148 file_buf[num_bytes] = '\0'; 149 close(fh); 150 151 /* Parse file */ 152 model = models + sizeof models / sizeof models[0]; 153 for (p = file_buf; *p; ++p) { 154 /* Beginning of line */ 155 if (strncmp(p, "processor", sizeof "processor" - 1 ) != 0) continue; 156 157 m = strstr(p, "machine"); 158 if (m == NULL) continue; 159 160 p = m + sizeof "machine" - 1; 161 while (isspace(*p) || *p == '=') { 162 if (*p == '\n') goto next_line; 163 ++p; 164 } 165 166 model_name = p; 167 for (n = 0; n < sizeof models / sizeof models[0]; ++n) { 168 model_info *mm = models + n; 169 size_t len = strlen(mm->cpuinfo_name); 170 if (strncmp(mm->cpuinfo_name, model_name, len) == 0 && 171 isspace(model_name[len])) { 172 /* In case there are different CPUs in this cluster return the 173 one with the dewest capabilities ("oldest" model). */ 174 if (mm < model) model = mm; 175 p = model_name + len; 176 break; 177 } 178 } 179 /* Skip until end-of-line */ 180 while (*p != '\n') 181 ++p; 182 next_line: ; 183 } 184 185 free(file_buf); 186 187 if (model == models + sizeof models / sizeof models[0]) return NULL; 188 189 return model; 190 } 191 192 static int go(char *feature, char *cpu) 193 { 194 unsigned long long facilities; 195 unsigned long long match; 196 model_info *host, *from, *to, *p; 197 char *colon; 198 199 facilities = stfle(); 200 201 if (strcmp(feature, "s390x-zarch") == 0 ) { 202 match = (facilities & (1ULL << 62) && (facilities & (1ULL << 61))); 203 } else if (strcmp(feature, "s390x-n3") == 0 ) { 204 match = (facilities & (1ULL << 63)); 205 } else if (strcmp(feature, "s390x-stfle") == 0 ) { 206 match = (facilities & (1ULL << 56)); 207 } else if (strcmp(feature, "s390x-ldisp") == 0 ) { 208 match = (facilities & (1ULL << 45) && (facilities & (1ULL << 44))); 209 } else if (strcmp(feature, "s390x-eimm") == 0 ) { 210 match = (facilities & (1ULL << 42)); 211 } else if (strcmp(feature, "s390x-stckf") == 0 ) { 212 match = (facilities & (1ULL << 38)); 213 } else if (strcmp(feature, "s390x-genins") == 0 ) { 214 match = (facilities & (1ULL << 29)); 215 } else if (strcmp(feature, "s390x-exrl") == 0 ) { 216 match = (facilities & (1ULL << 28)); 217 } else { 218 return 2; // Unrecognised feature. 219 } 220 221 if (match == 0) return 1; // facility not provided 222 223 /* Host provides facility. If no CPU was specified, we're done. */ 224 if (cpu == NULL) return 0; 225 226 host = get_host(); 227 if (host == NULL) return 2; // unknown model 228 229 // printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name); 230 231 /* Determine interval of models in which to search for HOST. */ 232 from = to = NULL; 233 colon = strchr(cpu, ':'); 234 235 if (colon == NULL) { 236 // match exact 237 from = to = locate_model(cpu); 238 } else if (colon == cpu) { 239 // :NAME match machines up to and including CPU 240 from = models; 241 to = locate_model(cpu + 1); 242 } else if (colon[1] == '\0') { 243 // NAME: match machines beginning with CPU or later 244 *colon = '\0'; 245 from = locate_model(cpu); 246 to = models + sizeof models / sizeof models[0] - 1; 247 *colon = ':'; 248 } else { 249 // NAME:NAME match machines in interval 250 *colon = '\0'; 251 from = locate_model(cpu); 252 to = locate_model(colon + 1); 253 *colon = ':'; 254 } 255 256 if (from == NULL || to == NULL || from > to) { 257 fprintf(stderr, "invalid cpu specification '%s'\n", cpu); 258 return 3; 259 } 260 261 #if 0 262 printf("from %s (%s) to %s (%s)\n", from->cpuinfo_name, from->real_name, 263 to->cpuinfo_name, to->real_name); 264 #endif 265 266 /* Search for HOST. */ 267 for (p = from; p <= to; ++p) { 268 if (p == host) return 0; 269 } 270 271 return 1; // host does not match CPU specification 272 } 273 274 #else 275 276 static int go(char *feature, char *cpu) 277 { 278 return 2; // Feature not recognised (non-s390x machine!) 279 } 280 281 #endif 282 283 284 //--------------------------------------------------------------------------- 285 // main 286 //--------------------------------------------------------------------------- 287 int main(int argc, char **argv) 288 { 289 int rc; 290 291 if (argc < 2 || argc > 3) { 292 fprintf( stderr, "usage: s390x_features <feature> [<machine-model>]\n" ); 293 exit(3); // Usage error. 294 } 295 296 rc = go(argv[1], argv[2]); 297 298 // printf("rc = %d\n", rc); 299 300 return rc; 301 } 302