Home | History | Annotate | Download | only in tests
      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