~ubuntu-branches/ubuntu/trusty/mstflint/trusty-updates

« back to all changes in this revision

Viewing changes to vpd.c

  • Committer: Bazaar Package Importer
  • Author(s): Benoit Mortier
  • Date: 2010-03-30 00:19:00 UTC
  • Revision ID: james.westby@ubuntu.com-20100330001900-a4oyvb4ioi6w6gvh
Tags: upstream-1.4-OFED-1.4.2
ImportĀ upstreamĀ versionĀ 1.4-OFED-1.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *
 
3
 *  vpd.c - PCI VPD (Vital Product Data) parser
 
4
 *
 
5
 * Author: Michael S. Tsirkin <mst@mellanox.co.il>
 
6
 *
 
7
 * Copyright (c) 2007 Mellanox Technologies Ltd.  All rights reserved.
 
8
 *
 
9
 * This software is available to you under a choice of one of two
 
10
 * licenses.  You may choose to be licensed under the terms of the GNU
 
11
 * General Public License (GPL) Version 2, available from the file
 
12
 * COPYING in the main directory of this source tree, or the
 
13
 * OpenIB.org BSD license below:
 
14
 *
 
15
 *     Redistribution and use in source and binary forms, with or
 
16
 *     without modification, are permitted provided that the following
 
17
 *     conditions are met:
 
18
 *
 
19
 *      - Redistributions of source code must retain the above
 
20
 *        copyright notice, this list of conditions and the following
 
21
 *        disclaimer.
 
22
 *
 
23
 *      - Redistributions in binary form must reproduce the above
 
24
 *        copyright notice, this list of conditions and the following
 
25
 *        disclaimer in the documentation and/or other materials
 
26
 *        provided with the distribution.
 
27
 *
 
28
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 
29
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 
30
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 
31
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 
32
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 
33
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 
34
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 
35
 * SOFTWARE.
 
36
 */
 
37
 
 
38
#define _XOPEN_SOURCE 500
 
39
#include <string.h>
 
40
#include <sys/stat.h>
 
41
#include <fcntl.h>
 
42
#include <unistd.h>
 
43
#include <stdio.h>
 
44
#include <stdlib.h>
 
45
#include <errno.h>
 
46
#include <libgen.h>
 
47
#include <sys/times.h>
 
48
 
 
49
/* pread is non-blocking, so we loop until we find data.  Unfortunately, 
 
50
 * we can loop forever if the HCA is crashed or if the wrong device is
 
51
 * specified as an argument. So, we set time outs.
 
52
 */
 
53
static clock_t ticks_per_sec, start_t, curr_t, timeout_t = 5;
 
54
 
 
55
struct vpd_cap {
 
56
        unsigned char id;
 
57
        unsigned char ptr;
 
58
        unsigned char addr_low;
 
59
        unsigned char addr_high_flag;
 
60
        unsigned char data[4];
 
61
};
 
62
 
 
63
enum {
 
64
        VPD_CAP_ID           = 0x03,
 
65
        PCI_CAP_PTR          = 0x34,
 
66
        PCI_HDR_SIZE         = 0x40,
 
67
        VPD_FLAG             = 0x80,
 
68
        VPD_FLAG_WRITE_START = 0x80,
 
69
        VPD_FLAG_WRITE_READY = 0x00,
 
70
        VPD_FLAG_READ_START  = 0x00,
 
71
        VPD_FLAG_READ_READY  = 0x80,
 
72
        VPD_ADDR_OFFSET      = 0x02,
 
73
        VPD_DATA_OFFSET      = 0x04,
 
74
        VPD_MAX_SIZE         = 1<<16
 
75
};
 
76
 
 
77
struct vpd_field {
 
78
        unsigned char keyword[2];
 
79
        unsigned char length;
 
80
        unsigned char data[0];
 
81
} __attribute__((packed));
 
82
 
 
83
union vpd_data {
 
84
        struct vpd_field fields[0];
 
85
        unsigned char bytes[0];
 
86
} __attribute__((packed));
 
87
 
 
88
struct vpd_small_data {
 
89
        unsigned char type;
 
90
        union vpd_data data;
 
91
} __attribute__((packed));
 
92
 
 
93
struct vpd_large_data {
 
94
        unsigned char type;
 
95
        unsigned char length_lsb;
 
96
        unsigned char length_msb;
 
97
        union vpd_data data;
 
98
} __attribute__((packed));
 
99
 
 
100
union vpd_data_type {
 
101
        unsigned char type;
 
102
        struct vpd_small_data small;
 
103
        struct vpd_large_data large;
 
104
        unsigned char bytes[0];
 
105
} __attribute__((packed));
 
106
 
 
107
typedef unsigned char vpd_t[VPD_MAX_SIZE];
 
108
 
 
109
#define VPD_TAG_LARGE(d) ((d)->type & 0x80)
 
110
 
 
111
#define VPD_TAG_LENGTH(d) (VPD_TAG_LARGE(d) ? ((((d)->large.length_msb) << 8) | \
 
112
                                               ((d)->large.length_lsb)) :       \
 
113
                           ((d)->type & 0x7))
 
114
 
 
115
#define VPD_TAG_HEAD(d) (VPD_TAG_LARGE(d) ? sizeof (d)->large : sizeof (d)->small)
 
116
 
 
117
#define VPD_TAG_NAME(d) (VPD_TAG_LARGE(d) ? ((d)->type & 0x7F) : ((d)->type >> 3))
 
118
 
 
119
#define VPD_TAG_DATA(d) (VPD_TAG_LARGE(d) ? &(d)->large.data : &(d)->small.data)
 
120
 
 
121
enum {
 
122
        VPD_TAG_ID = 0x02, /* Product name of te device */
 
123
        VPD_TAG_R  = 0x10, /* Read-only keywords */
 
124
        VPD_TAG_W  = 0x11, /* Read-write keywords */
 
125
        VPD_TAG_F  = 0x0F, /* End Tag */
 
126
};
 
127
 
 
128
#define VPD_FIELD_CHECKSUM "RV"
 
129
 
 
130
int pci_find_capability(int device, int cap_id)
 
131
{
 
132
        unsigned offset;
 
133
        unsigned char visited[256] = {}; /* Prevent infinite loops */
 
134
        unsigned char data[2];
 
135
        int ret;
 
136
 
 
137
        ret = pread(device, data, 1, PCI_CAP_PTR);
 
138
        if (ret != 1)
 
139
                return 0;
 
140
        offset = data[0];
 
141
 
 
142
        for(;;) {
 
143
                if (offset < PCI_HDR_SIZE)
 
144
                        return 0;
 
145
 
 
146
                ret = pread(device, data, sizeof data, offset);
 
147
                if (ret != sizeof data)
 
148
                        return 0;
 
149
 
 
150
                visited[offset] = 1;
 
151
 
 
152
                if (data[0] == cap_id)
 
153
                        return offset;
 
154
 
 
155
                offset = data[1];
 
156
                if (visited[offset])
 
157
                        return 0;
 
158
        }
 
159
}
 
160
 
 
161
int pci_read_vpd_dword(int device, int vpd_cap_offset, unsigned offset, unsigned char data[4])
 
162
{
 
163
        unsigned char addr_flag[2];
 
164
        int ret;
 
165
 
 
166
        if (offset >= VPD_MAX_SIZE || (offset & 0x3))
 
167
                return -1;
 
168
 
 
169
        addr_flag[0] = (offset) & ~0x3;
 
170
        addr_flag[1] = ((offset) >> 8) | VPD_FLAG_READ_START;
 
171
 
 
172
        ret = pwrite(device, addr_flag, sizeof addr_flag,
 
173
                     vpd_cap_offset + VPD_ADDR_OFFSET);
 
174
 
 
175
        if (ret != sizeof addr_flag)
 
176
                return ret;
 
177
 
 
178
        start_t = times(NULL);
 
179
        while((addr_flag[1] & VPD_FLAG) != VPD_FLAG_READ_READY) {
 
180
                curr_t = times(NULL);
 
181
                if ((curr_t - start_t) / ticks_per_sec > timeout_t) {
 
182
                        fprintf(stderr, "-E- VPD read timeout\n");
 
183
                        return -EIO;
 
184
                }
 
185
 
 
186
                ret = pread(device, addr_flag, sizeof addr_flag,
 
187
                             vpd_cap_offset + VPD_ADDR_OFFSET);
 
188
                if (ret != sizeof addr_flag)
 
189
                        return ret;
 
190
        }
 
191
 
 
192
        ret = pread(device, data, sizeof data, vpd_cap_offset + VPD_DATA_OFFSET);
 
193
        if (ret != sizeof data)
 
194
                return ret;
 
195
 
 
196
        return 0;
 
197
}
 
198
 
 
199
int vpd_read(int device, vpd_t vpd)
 
200
{
 
201
        unsigned offset;
 
202
        int ret;
 
203
        int vpd_cap_offset;
 
204
        vpd_cap_offset = pci_find_capability(device, VPD_CAP_ID);
 
205
        if (!vpd_cap_offset)
 
206
                return -1;
 
207
 
 
208
        for (offset = 0; offset < VPD_MAX_SIZE; offset += 0x4) {
 
209
                ret = pci_read_vpd_dword(device, vpd_cap_offset, offset, vpd + offset);
 
210
                if (ret)
 
211
                        return ret;
 
212
        }
 
213
 
 
214
        return 0;
 
215
}
 
216
 
 
217
/* Verify that keywords in R and W sections fit in length. */
 
218
int vpd_check_one(union vpd_data_type *d, unsigned offset)
 
219
{
 
220
        unsigned i;
 
221
        struct vpd_field *field;
 
222
 
 
223
        if (VPD_TAG_NAME(d) == VPD_TAG_R ||
 
224
            VPD_TAG_NAME(d) == VPD_TAG_W)
 
225
                for (i = 0; i < VPD_TAG_LENGTH(d); i += 0x3 + field->length) {
 
226
                        field = (struct vpd_field *)(VPD_TAG_DATA(d)->bytes + i);
 
227
                        if (i + 0x3 + field->length > VPD_TAG_LENGTH(d)) {
 
228
                                fprintf(stderr, "-E- Offset 0x%x+0x%x: "
 
229
                                        "field length 0x%x exceeds total 0x%x\n",
 
230
                                        offset, i, field->length, VPD_TAG_LENGTH(d));
 
231
                                return -1;
 
232
                        }
 
233
                }
 
234
        return 0;
 
235
}
 
236
 
 
237
/* Find length to checksum. */
 
238
void vpd_checksum_length(union vpd_data_type *d, unsigned offset, unsigned *checksum_len)
 
239
{
 
240
        unsigned i;
 
241
        struct vpd_field *field;
 
242
 
 
243
        if (VPD_TAG_NAME(d) != VPD_TAG_R)
 
244
                return;
 
245
 
 
246
        for (i = 0; i < VPD_TAG_LENGTH(d); i += 0x3 + field->length) {
 
247
                field = (struct vpd_field *)(VPD_TAG_DATA(d)->bytes + i);
 
248
                if (!memcmp(VPD_FIELD_CHECKSUM, field->keyword,
 
249
                            sizeof field->keyword)) {
 
250
                        *checksum_len = offset + VPD_TAG_HEAD(d) + i + 0x3 + 1;
 
251
                }
 
252
        }
 
253
}
 
254
 
 
255
void vpd_show_field(FILE *f, struct vpd_field *field)
 
256
{
 
257
        int i;
 
258
 
 
259
        if (!memcmp(VPD_FIELD_CHECKSUM, field->keyword, sizeof field->keyword))
 
260
                return;
 
261
        fputc(field->keyword[0], f);
 
262
        fputc(field->keyword[1], f);
 
263
        fputs(": ", f);
 
264
        for (i = 0; i < field->length; ++i) {
 
265
                if (!field->data[i])
 
266
                        break;
 
267
                fputc(field->data[i], f);
 
268
        }
 
269
        fputc('\n', f);
 
270
}
 
271
 
 
272
void vpd_show_fields(FILE *f, union vpd_data_type *d, const char *keyword)
 
273
{
 
274
        struct vpd_field *field;
 
275
        int i;
 
276
 
 
277
        for (i = 0; i < VPD_TAG_LENGTH(d); i += 0x3 + field->length) {
 
278
                field = (struct vpd_field *)(VPD_TAG_DATA(d)->bytes + i);
 
279
                if (!keyword || !memcmp(keyword, field->keyword, sizeof field->keyword))
 
280
                        vpd_show_field(f, field);
 
281
        }
 
282
}
 
283
 
 
284
void vpd_show_id(FILE *f, union vpd_data_type *d)
 
285
{
 
286
        int i;
 
287
 
 
288
        fputs("ID: ", f);
 
289
        for (i = 0; i < VPD_TAG_LENGTH(d); ++i)
 
290
                fputc(VPD_TAG_DATA(d)->bytes[i], f);
 
291
        fputc('\n', f);
 
292
}
 
293
 
 
294
void vpd_show_one(FILE *f, union vpd_data_type* d, const char *keyword)
 
295
{
 
296
        switch(VPD_TAG_NAME(d)) {
 
297
        case VPD_TAG_ID:
 
298
                if (!keyword || !memcmp("ID", keyword, 2))
 
299
                        vpd_show_id(f, d);
 
300
                break;
 
301
        case VPD_TAG_R:
 
302
                vpd_show_fields(f, d, keyword);
 
303
                break;
 
304
        case VPD_TAG_W:
 
305
                vpd_show_fields(f, d, keyword);
 
306
                break;
 
307
        case VPD_TAG_F:
 
308
                break;
 
309
        default:
 
310
                if (!keyword)
 
311
                        fprintf(f, "??: 0x%x\n", VPD_TAG_NAME(d));
 
312
        }
 
313
}
 
314
 
 
315
int vpd_check(vpd_t vpd, int checksum)
 
316
{
 
317
        unsigned char b;
 
318
        int i;
 
319
        unsigned offset;
 
320
        int rc;
 
321
        union vpd_data_type *d = NULL;
 
322
        unsigned checksum_len = 0;
 
323
 
 
324
        for (offset = 0; offset < VPD_MAX_SIZE && (!d || VPD_TAG_NAME(d) != VPD_TAG_F);
 
325
             offset += VPD_TAG_HEAD(d) + VPD_TAG_LENGTH(d)) {
 
326
                d = (union vpd_data_type *)(vpd + offset);
 
327
                rc = vpd_check_one(d, offset);
 
328
                if (rc)
 
329
                        return rc;
 
330
 
 
331
                vpd_checksum_length(d, offset, &checksum_len);
 
332
        }
 
333
 
 
334
        if (VPD_TAG_NAME(d) != VPD_TAG_F) {
 
335
                fprintf(stderr, "-E- Mandatory End(0xF) tag not found.\n");
 
336
                return 1;
 
337
        }
 
338
 
 
339
        if (!checksum)
 
340
                return 0;
 
341
 
 
342
        if (!checksum_len) {
 
343
                fprintf(stderr, "-E- Mandatory checksum(RV) field not found.\n");
 
344
                return 1;
 
345
        }
 
346
 
 
347
        b = 0;
 
348
        for (i = 0; i < checksum_len; ++i)
 
349
                b+= vpd[i];
 
350
 
 
351
        if (b) {
 
352
                fprintf(stderr, "-E- Len 0x%x: checksum mismatch: 0x%x\n",
 
353
                        checksum_len, b);
 
354
                return 1;
 
355
        }
 
356
 
 
357
        return 0;
 
358
}
 
359
 
 
360
void vpd_show(FILE *f, vpd_t vpd, const char *keyword)
 
361
{
 
362
        unsigned offset;
 
363
        union vpd_data_type *d = NULL;
 
364
        for (offset = 0; !d || VPD_TAG_NAME(d) != VPD_TAG_F;
 
365
             offset += VPD_TAG_HEAD(d) + VPD_TAG_LENGTH(d)) {
 
366
                d = (union vpd_data_type *)(vpd + offset);
 
367
                vpd_show_one(f, d, keyword);
 
368
        }
 
369
}
 
370
 
 
371
int pci_parse_name(const char *name, char buf[4096])
 
372
{
 
373
        int domain, bus, dev, func, tmp;
 
374
        struct stat dummybuf;
 
375
 
 
376
        if (*name == '/') {
 
377
                if (strlen(name) >= 4096)
 
378
                        return -1;
 
379
                strcpy(buf, name);
 
380
                return 0;
 
381
        }
 
382
 
 
383
        if (sscanf(name,"mthca%x", & tmp) == 1 ||
 
384
            sscanf(name,"mlx4_%x", & tmp) == 1) {
 
385
                char mbuf[4096];
 
386
                char pbuf[4096];
 
387
                char *base;
 
388
 
 
389
                tmp = snprintf(mbuf, sizeof mbuf, "/sys/class/infiniband/%s/device", name);
 
390
                if (tmp <= 0 || tmp >= (int)sizeof mbuf) {
 
391
                        fprintf(stderr,"-E- Unable to print device name %s\n", name);
 
392
                        return 1;
 
393
                }
 
394
 
 
395
                if (readlink(mbuf, pbuf, sizeof pbuf) < 0) {
 
396
                        perror("-E- read link");
 
397
                        fprintf(stderr,"-E- Unable to read link %s\n", mbuf);
 
398
                        return 1;
 
399
                }
 
400
 
 
401
                base = basename(pbuf);
 
402
                if (!base)
 
403
                        return 1;
 
404
                if (sscanf(base, "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4)
 
405
                        return 1;
 
406
        } else if (sscanf(name, "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4) {
 
407
                domain = 0;
 
408
                if (sscanf(name, "%x:%x.%x", &bus, &dev, &func) != 3)
 
409
                        return -2;
 
410
        }
 
411
        
 
412
        snprintf(buf, 4096, "/proc/bus/pci/%2.2x/%2.2x.%1.1x", bus, dev, func);
 
413
        if (stat(buf, &dummybuf))
 
414
                snprintf(buf, 4096, "/proc/bus/pci/%4.4x:%2.2x/%2.2x.%1.1x",
 
415
                         domain, bus,dev,func);
 
416
 
 
417
        if (stat(buf, &dummybuf))
 
418
                return -3;
 
419
 
 
420
        return 0;
 
421
}
 
422
 
 
423
int vpd_open(const char *name)
 
424
{
 
425
        int fd;
 
426
        char buf[4096];
 
427
 
 
428
        if (pci_parse_name(name, buf)) {
 
429
                fprintf(stderr, "-E- Unable to parse device name %s\n", name);
 
430
                return -1;
 
431
        }
 
432
 
 
433
        fd = open(buf, O_RDWR);
 
434
        if (fd < 0) {
 
435
                fprintf(stderr, "-E- Unable to open file %s: %s\n", buf, strerror(errno));
 
436
        }
 
437
        return fd;
 
438
}
 
439
 
 
440
int main(int argc, char **argv)
 
441
{
 
442
        const char *name;
 
443
        int fd;
 
444
        int i;
 
445
        int rc = 0;
 
446
        vpd_t d;
 
447
        int m = 0;
 
448
        int n = 0;
 
449
 
 
450
        if (argc < 2) {
 
451
                rc = 1;
 
452
                goto usage;
 
453
        }
 
454
 
 
455
        ticks_per_sec = sysconf(_SC_CLK_TCK);
 
456
 
 
457
        do
 
458
        {
 
459
                i=getopt(argc, argv, "mnt:");
 
460
                if (i<0) {
 
461
                        break;
 
462
                }
 
463
 
 
464
                switch (i) {
 
465
                        case 'm':
 
466
                                m=1;
 
467
                                break;
 
468
                        case 'n':
 
469
                                n=1;
 
470
                                break;
 
471
                        case 't':
 
472
                                timeout_t = strtol(optarg, NULL, 0);
 
473
                                break;
 
474
                        default:
 
475
                                goto usage;
 
476
                }
 
477
        } while (1 == 1);
 
478
                                
 
479
        name = argv[optind];
 
480
        argc -= optind;
 
481
        argv += optind;
 
482
 
 
483
        if (!strcmp("-", name)) {
 
484
                if (fread(d, VPD_MAX_SIZE, 1, stdin) != 1)
 
485
                        return 3;
 
486
        } else {
 
487
                fd = vpd_open(name);
 
488
                if (fd < 0)
 
489
                        return 4;
 
490
 
 
491
                if (vpd_read(fd, d))
 
492
                        return 5;
 
493
        }
 
494
 
 
495
        if (m)
 
496
                return fwrite(d, VPD_MAX_SIZE, 1, stdout) != 1;
 
497
 
 
498
        if (vpd_check(d, !n))
 
499
                return 6;
 
500
 
 
501
        if (argc == 1)
 
502
                vpd_show(stdout, d, NULL);
 
503
        else
 
504
                for (i = 0; i < argc - 1; ++i) {
 
505
                        if (!strcmp(argv[i + 1], "--"))
 
506
                            continue;
 
507
                        vpd_show(stdout, d, argv[i + 1]);
 
508
                }
 
509
 
 
510
        return 0;
 
511
 
 
512
usage:
 
513
        fprintf(stderr, "Usage: %s [-m|-n] [-t ##] <file> [-- keyword ...]\n", argv[0]);
 
514
        fprintf(stderr, "-m\tDump raw VPD data to stdout.\n");
 
515
        fprintf(stderr, "-n\tDo not validate check sum.\n");
 
516
        fprintf(stderr, "-t ##\tTime out after ## seconds. (Default is 30.)\n\n");
 
517
        fprintf(stderr, "file\tThe PCI id number of the HCA (for example, \"2:00.0\"),\n");
 
518
        fprintf(stderr, "\tthe device name (such as \"mlx4_0\")\n");
 
519
        fprintf(stderr, "\tthe absolute path to the device (\"/sys/class/infiniband/mlx4_0/device\")\n");
 
520
        fprintf(stderr, "\tor '-' to read VPD data from the standard input.\n\n");
 
521
        fprintf(stderr, "keyword(s): Only display the requested information. (ID, PN, EC, SN, etc...)\n");
 
522
        return rc;
 
523
}