summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorflorian <florian@a5019735-40e9-0310-863c-91ae7b9d1cf9>2011-07-07 22:09:41 +0000
committerflorian <florian@a5019735-40e9-0310-863c-91ae7b9d1cf9>2011-07-07 22:09:41 +0000
commitec82dd9de08eaf9d6d5c6b1ebaec53039e371903 (patch)
treea97ee569ebf383bbd9204f7e2e036f668b40d567 /tests
parent51331e19f0e544da9a118ad50e4bca174713a132 (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.c242
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;
}