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 <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