3
* vpd.c - PCI VPD (Vital Product Data) parser
5
* Author: Michael S. Tsirkin <mst@mellanox.co.il>
7
* Copyright (c) 2007 Mellanox Technologies Ltd. All rights reserved.
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:
15
* Redistribution and use in source and binary forms, with or
16
* without modification, are permitted provided that the following
19
* - Redistributions of source code must retain the above
20
* copyright notice, this list of conditions and the following
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.
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
38
#define _XOPEN_SOURCE 500
47
#include <sys/times.h>
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.
53
static clock_t ticks_per_sec, start_t, curr_t, timeout_t = 5;
58
unsigned char addr_low;
59
unsigned char addr_high_flag;
60
unsigned char data[4];
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,
78
unsigned char keyword[2];
80
unsigned char data[0];
81
} __attribute__((packed));
84
struct vpd_field fields[0];
85
unsigned char bytes[0];
86
} __attribute__((packed));
88
struct vpd_small_data {
91
} __attribute__((packed));
93
struct vpd_large_data {
95
unsigned char length_lsb;
96
unsigned char length_msb;
98
} __attribute__((packed));
100
union vpd_data_type {
102
struct vpd_small_data small;
103
struct vpd_large_data large;
104
unsigned char bytes[0];
105
} __attribute__((packed));
107
typedef unsigned char vpd_t[VPD_MAX_SIZE];
109
#define VPD_TAG_LARGE(d) ((d)->type & 0x80)
111
#define VPD_TAG_LENGTH(d) (VPD_TAG_LARGE(d) ? ((((d)->large.length_msb) << 8) | \
112
((d)->large.length_lsb)) : \
115
#define VPD_TAG_HEAD(d) (VPD_TAG_LARGE(d) ? sizeof (d)->large : sizeof (d)->small)
117
#define VPD_TAG_NAME(d) (VPD_TAG_LARGE(d) ? ((d)->type & 0x7F) : ((d)->type >> 3))
119
#define VPD_TAG_DATA(d) (VPD_TAG_LARGE(d) ? &(d)->large.data : &(d)->small.data)
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 */
128
#define VPD_FIELD_CHECKSUM "RV"
130
int pci_find_capability(int device, int cap_id)
133
unsigned char visited[256] = {}; /* Prevent infinite loops */
134
unsigned char data[2];
137
ret = pread(device, data, 1, PCI_CAP_PTR);
143
if (offset < PCI_HDR_SIZE)
146
ret = pread(device, data, sizeof data, offset);
147
if (ret != sizeof data)
152
if (data[0] == cap_id)
161
int pci_read_vpd_dword(int device, int vpd_cap_offset, unsigned offset, unsigned char data[4])
163
unsigned char addr_flag[2];
166
if (offset >= VPD_MAX_SIZE || (offset & 0x3))
169
addr_flag[0] = (offset) & ~0x3;
170
addr_flag[1] = ((offset) >> 8) | VPD_FLAG_READ_START;
172
ret = pwrite(device, addr_flag, sizeof addr_flag,
173
vpd_cap_offset + VPD_ADDR_OFFSET);
175
if (ret != sizeof addr_flag)
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");
186
ret = pread(device, addr_flag, sizeof addr_flag,
187
vpd_cap_offset + VPD_ADDR_OFFSET);
188
if (ret != sizeof addr_flag)
192
ret = pread(device, data, sizeof data, vpd_cap_offset + VPD_DATA_OFFSET);
193
if (ret != sizeof data)
199
int vpd_read(int device, vpd_t vpd)
204
vpd_cap_offset = pci_find_capability(device, VPD_CAP_ID);
208
for (offset = 0; offset < VPD_MAX_SIZE; offset += 0x4) {
209
ret = pci_read_vpd_dword(device, vpd_cap_offset, offset, vpd + offset);
217
/* Verify that keywords in R and W sections fit in length. */
218
int vpd_check_one(union vpd_data_type *d, unsigned offset)
221
struct vpd_field *field;
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));
237
/* Find length to checksum. */
238
void vpd_checksum_length(union vpd_data_type *d, unsigned offset, unsigned *checksum_len)
241
struct vpd_field *field;
243
if (VPD_TAG_NAME(d) != VPD_TAG_R)
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;
255
void vpd_show_field(FILE *f, struct vpd_field *field)
259
if (!memcmp(VPD_FIELD_CHECKSUM, field->keyword, sizeof field->keyword))
261
fputc(field->keyword[0], f);
262
fputc(field->keyword[1], f);
264
for (i = 0; i < field->length; ++i) {
267
fputc(field->data[i], f);
272
void vpd_show_fields(FILE *f, union vpd_data_type *d, const char *keyword)
274
struct vpd_field *field;
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);
284
void vpd_show_id(FILE *f, union vpd_data_type *d)
289
for (i = 0; i < VPD_TAG_LENGTH(d); ++i)
290
fputc(VPD_TAG_DATA(d)->bytes[i], f);
294
void vpd_show_one(FILE *f, union vpd_data_type* d, const char *keyword)
296
switch(VPD_TAG_NAME(d)) {
298
if (!keyword || !memcmp("ID", keyword, 2))
302
vpd_show_fields(f, d, keyword);
305
vpd_show_fields(f, d, keyword);
311
fprintf(f, "??: 0x%x\n", VPD_TAG_NAME(d));
315
int vpd_check(vpd_t vpd, int checksum)
321
union vpd_data_type *d = NULL;
322
unsigned checksum_len = 0;
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);
331
vpd_checksum_length(d, offset, &checksum_len);
334
if (VPD_TAG_NAME(d) != VPD_TAG_F) {
335
fprintf(stderr, "-E- Mandatory End(0xF) tag not found.\n");
343
fprintf(stderr, "-E- Mandatory checksum(RV) field not found.\n");
348
for (i = 0; i < checksum_len; ++i)
352
fprintf(stderr, "-E- Len 0x%x: checksum mismatch: 0x%x\n",
360
void vpd_show(FILE *f, vpd_t vpd, const char *keyword)
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);
371
int pci_parse_name(const char *name, char buf[4096])
373
int domain, bus, dev, func, tmp;
374
struct stat dummybuf;
377
if (strlen(name) >= 4096)
383
if (sscanf(name,"mthca%x", & tmp) == 1 ||
384
sscanf(name,"mlx4_%x", & tmp) == 1) {
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);
395
if (readlink(mbuf, pbuf, sizeof pbuf) < 0) {
396
perror("-E- read link");
397
fprintf(stderr,"-E- Unable to read link %s\n", mbuf);
401
base = basename(pbuf);
404
if (sscanf(base, "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4)
406
} else if (sscanf(name, "%x:%x:%x.%x", &domain, &bus, &dev, &func) != 4) {
408
if (sscanf(name, "%x:%x.%x", &bus, &dev, &func) != 3)
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);
417
if (stat(buf, &dummybuf))
423
int vpd_open(const char *name)
428
if (pci_parse_name(name, buf)) {
429
fprintf(stderr, "-E- Unable to parse device name %s\n", name);
433
fd = open(buf, O_RDWR);
435
fprintf(stderr, "-E- Unable to open file %s: %s\n", buf, strerror(errno));
440
int main(int argc, char **argv)
455
ticks_per_sec = sysconf(_SC_CLK_TCK);
459
i=getopt(argc, argv, "mnt:");
472
timeout_t = strtol(optarg, NULL, 0);
483
if (!strcmp("-", name)) {
484
if (fread(d, VPD_MAX_SIZE, 1, stdin) != 1)
496
return fwrite(d, VPD_MAX_SIZE, 1, stdout) != 1;
498
if (vpd_check(d, !n))
502
vpd_show(stdout, d, NULL);
504
for (i = 0; i < argc - 1; ++i) {
505
if (!strcmp(argv[i + 1], "--"))
507
vpd_show(stdout, d, argv[i + 1]);
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");