diff options
author | florian <florian@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2011-07-07 22:09:41 +0000 |
---|---|---|
committer | florian <florian@a5019735-40e9-0310-863c-91ae7b9d1cf9> | 2011-07-07 22:09:41 +0000 |
commit | ec82dd9de08eaf9d6d5c6b1ebaec53039e371903 (patch) | |
tree | a97ee569ebf383bbd9204f7e2e036f668b40d567 /tests | |
parent | 51331e19f0e544da9a118ad50e4bca174713a132 (diff) |
Allow optional specification of cpu models on the command line
and test whether the host machine matches one of those. This is
needed to fix Bug #271776.
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@11862 a5019735-40e9-0310-863c-91ae7b9d1cf9
Diffstat (limited to 'tests')
-rw-r--r-- | tests/s390x_features.c | 242 |
1 files changed, 225 insertions, 17 deletions
diff --git a/tests/s390x_features.c b/tests/s390x_features.c index 1e9e8e21..302d1677 100644 --- a/tests/s390x_features.c +++ b/tests/s390x_features.c @@ -1,18 +1,38 @@ +/* -*- mode: C; c-basic-offset: 3; -*- */ + #include <setjmp.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <assert.h> +#include <ctype.h> // isspace +#include <fcntl.h> // open +#include <unistd.h> // lseek +#include <sys/stat.h> // S_IRUSR // This file determines s390x features a processor supports. // // We return: -// - 0 if the machine matches the asked-for feature. -// - 1 if the machine does not. +// - 0 if the machine provides the asked-for feature and the cpu +// model, if specified, matches the machine +// - 1 the machine does not provide the asked-for feature or the +// cpu model, if specified, does not match the machine // - 2 if the asked-for feature isn't recognised (this will be the case for // any feature if run on a non-s390x machine). +// - 2 for an unknown cpu model in /proc/cpu_info // - 3 if there was a usage error (it also prints an error message). +// +// USAGE: +// +// s390x_features <feature> [<machine-model>] +// +// The machine_model is optional and it can be something like: +// +// z9 -- Host needs to be a z9 (and nothing else) +// z9: -- Host needs to be a z9 or any later model +// :z9 -- Host needs to be a model up to and including z9 +// z900:z9 -- Host needs to be at least a z900 and at most a z9. +// Any model in between is OK, too. jmp_buf env; @@ -40,39 +60,220 @@ unsigned long long stfle(void) } } -static int go(char* cpu) + +/* Read /proc/cpuinfo. Look for lines like these + + processor 0: version = FF, identification = 0117C9, machine = 2064 + + and return the machine model or NULL on error. + Adapted from function VG_(get_machine_model) in coregrind/m_machine.c */ + +typedef struct { + const char *cpuinfo_name; + const char *real_name; +} model_info; + +/* Array needs to be sorted chronologically. Oldest to newest */ +model_info models[] = { + { "2064", "z900" }, + { "2066", "z800" }, + { "2084", "z990" }, + { "2086", "z890" }, + { "2094", "z9-ec" }, + { "2096", "z9-bc" }, + { "2097", "z10-ec" }, + { "2098", "z10-bc" }, + { "2817", "z196" }, +}; + + +/* Locate a machine model by name. Name can be either the cpuinfo + name or the external name. */ +static model_info *locate_model(const char *name) +{ + model_info *p; + + /* Try cpuinfo name first */ + for (p = models; p != models + sizeof models / sizeof models[0]; ++p) { + if (strcmp(p->cpuinfo_name, name) == 0) return p; // found it + } + + /* Now try external name */ + for (p = models; p != models + sizeof models / sizeof models[0]; ++p) { + if (strcmp(p->real_name, name) == 0) return p; // found it + } + + return NULL; +} + + +static model_info *get_host(void) +{ + int n, fh; + size_t num_bytes, file_buf_size; + char *p, *m, *model_name, *file_buf; + model_info *model; + + /* Slurp contents of /proc/cpuinfo into FILE_BUF */ + //fh = open("/proc/cpuinfo", O_RDONLY, S_IRUSR); + fh = open("/proc/cpuinfo", O_RDONLY, S_IRUSR); + if (fh < 0) return NULL; + + /* Determine the size of /proc/cpuinfo. + Work around broken-ness in /proc file system implementation. + fstat returns a zero size for /proc/cpuinfo although it is + claimed to be a regular file. */ + num_bytes = 0; + file_buf_size = 1000; + file_buf = malloc(file_buf_size + 1); + + while (42) { + n = read(fh, file_buf, file_buf_size); + if (n < 0) break; + + num_bytes += n; + if (n < file_buf_size) break; /* reached EOF */ + } + + if (n < 0) num_bytes = 0; /* read error; ignore contents */ + + if (num_bytes > file_buf_size) { + free(file_buf); + lseek(fh, 0, SEEK_SET); + file_buf = malloc(num_bytes + 1); + n = read(fh, file_buf, num_bytes); + if (n < 0) num_bytes = 0; + } + + file_buf[num_bytes] = '\0'; + close(fh); + + /* Parse file */ + model = models + sizeof models / sizeof models[0]; + for (p = file_buf; *p; ++p) { + /* Beginning of line */ + if (strncmp(p, "processor", sizeof "processor" - 1 ) != 0) continue; + + m = strstr(p, "machine"); + if (m == NULL) continue; + + p = m + sizeof "machine" - 1; + while (isspace(*p) || *p == '=') { + if (*p == '\n') goto next_line; + ++p; + } + + model_name = p; + for (n = 0; n < sizeof models / sizeof models[0]; ++n) { + model_info *mm = models + n; + size_t len = strlen(mm->cpuinfo_name); + if (strncmp(mm->cpuinfo_name, model_name, len) == 0 && + isspace(model_name[len])) { + /* In case there are different CPUs in this cluster return the + one with the dewest capabilities ("oldest" model). */ + if (mm < model) model = mm; + p = model_name + len; + break; + } + } + /* Skip until end-of-line */ + while (*p != '\n') + ++p; + next_line: ; + } + + free(file_buf); + + if (model == models + sizeof models / sizeof models[0]) return NULL; + + return model; +} + +static int go(char *feature, char *cpu) { unsigned long long facilities; unsigned long long match; + model_info *host, *from, *to, *p; + char *colon; facilities = stfle(); - if (strcmp(cpu, "s390x-zarch") == 0 ) { + if (strcmp(feature, "s390x-zarch") == 0 ) { match = (facilities & (1ULL << 62) && (facilities & (1ULL << 61))); - } else if (strcmp(cpu, "s390x-n3") == 0 ) { + } else if (strcmp(feature, "s390x-n3") == 0 ) { match = (facilities & (1ULL << 63)); - } else if (strcmp(cpu, "s390x-stfle") == 0 ) { + } else if (strcmp(feature, "s390x-stfle") == 0 ) { match = (facilities & (1ULL << 56)); - } else if (strcmp(cpu, "s390x-ldisp") == 0 ) { + } else if (strcmp(feature, "s390x-ldisp") == 0 ) { match = (facilities & (1ULL << 45) && (facilities & (1ULL << 44))); - } else if (strcmp(cpu, "s390x-eimm") == 0 ) { + } else if (strcmp(feature, "s390x-eimm") == 0 ) { match = (facilities & (1ULL << 42)); - } else if (strcmp(cpu, "s390x-stckf") == 0 ) { + } else if (strcmp(feature, "s390x-stckf") == 0 ) { match = (facilities & (1ULL << 38)); - } else if (strcmp(cpu, "s390x-genins") == 0 ) { + } else if (strcmp(feature, "s390x-genins") == 0 ) { match = (facilities & (1ULL << 29)); - } else if (strcmp(cpu, "s390x-exrl") == 0 ) { + } else if (strcmp(feature, "s390x-exrl") == 0 ) { match = (facilities & (1ULL << 28)); } else { return 2; // Unrecognised feature. } - return match == 0; + if (match == 0) return 1; // facility not provided + + /* Host provides facility. If no CPU was specified, we're done. */ + if (cpu == NULL) return 0; + + host = get_host(); + if (host == NULL) return 2; // unknown model + + // printf("host = %s (%s)\n", host->cpuinfo_name, host->real_name); + + /* Determine interval of models in which to search for HOST. */ + from = to = NULL; + colon = strchr(cpu, ':'); + + if (colon == NULL) { + // match exact + from = to = locate_model(cpu); + } else if (colon == cpu) { + // :NAME match machines up to and including CPU + from = models; + to = locate_model(cpu + 1); + } else if (colon[1] == '\0') { + // NAME: match machines beginning with CPU or later + *colon = '\0'; + from = locate_model(cpu); + to = models + sizeof models / sizeof models[0] - 1; + *colon = ':'; + } else { + // NAME:NAME match machines in interval + *colon = '\0'; + from = locate_model(cpu); + to = locate_model(colon + 1); + *colon = ':'; + } + + if (from == NULL || to == NULL || from > to) { + fprintf(stderr, "invalid cpu specification '%s'\n", cpu); + return 3; + } + +#if 0 + printf("from %s (%s) to %s (%s)\n", from->cpuinfo_name, from->real_name, + to->cpuinfo_name, to->real_name); +#endif + + /* Search for HOST. */ + for (p = from; p <= to; ++p) { + if (p == host) return 0; + } + + return 1; // host does not match CPU specification } #else -static int go(char* cpu) +static int go(char *feature, char *cpu) { return 2; // Feature not recognised (non-s390x machine!) } @@ -85,9 +286,16 @@ static int go(char* cpu) //--------------------------------------------------------------------------- int main(int argc, char **argv) { - if ( argc != 2 ) { - fprintf( stderr, "usage: s390x_features <feature>\n" ); + int rc; + + if (argc < 2 || argc > 3) { + fprintf( stderr, "usage: s390x_features <feature> [<machine-model>]\n" ); exit(3); // Usage error. } - return go(argv[1]); + + rc = go(argv[1], argv[2]); + + // printf("rc = %d\n", rc); + + return rc; } |