1c7a34993SMartin Mares /*
2c7a34993SMartin Mares * The PCI Utilities -- Show Vital Product Data
3c7a34993SMartin Mares *
42ba49657SMartin Mares * Copyright (c) 2008 Solarflare Communications
52ba49657SMartin Mares *
62ba49657SMartin Mares * Written by Ben Hutchings <[email protected]>
701f4caedSMartin Mares * Improved by Martin Mares <[email protected]>
8c7a34993SMartin Mares *
9*61829219SMartin Mares * Can be freely distributed and used under the terms of the GNU GPL v2+.
10*61829219SMartin Mares *
11*61829219SMartin Mares * SPDX-License-Identifier: GPL-2.0-or-later
12c7a34993SMartin Mares */
13c7a34993SMartin Mares
14c7a34993SMartin Mares #include <stdio.h>
15726b641bSMartin Mares #include <string.h>
16c7a34993SMartin Mares
17c7a34993SMartin Mares #include "lspci.h"
18c7a34993SMartin Mares
1901f4caedSMartin Mares /*
2001f4caedSMartin Mares * The list of all known VPD items and their formats.
2101f4caedSMartin Mares * Technically, this belongs to the pci.ids file, but the VPD does not seem
2201f4caedSMartin Mares * to be developed any longer, so we have chosen the easier way.
2301f4caedSMartin Mares */
2401f4caedSMartin Mares
2501f4caedSMartin Mares enum vpd_format {
2601f4caedSMartin Mares F_BINARY,
2701f4caedSMartin Mares F_TEXT,
2801f4caedSMartin Mares F_RESVD,
2901f4caedSMartin Mares F_RDWR,
3001f4caedSMartin Mares };
3101f4caedSMartin Mares
3201f4caedSMartin Mares static const struct vpd_item {
3301f4caedSMartin Mares byte id1, id2;
3401f4caedSMartin Mares byte format;
3501f4caedSMartin Mares const char *name;
3601f4caedSMartin Mares } vpd_items[] = {
3701f4caedSMartin Mares { 'C','P', F_BINARY, "Extended capability" },
3801f4caedSMartin Mares { 'E','C', F_TEXT, "Engineering changes" },
39b7ffb971SMartin Mares { 'M','N', F_TEXT, "Manufacture ID" },
4001f4caedSMartin Mares { 'P','N', F_TEXT, "Part number" },
4101f4caedSMartin Mares { 'R','V', F_RESVD, "Reserved" },
4201f4caedSMartin Mares { 'R','W', F_RDWR, "Read-write area" },
4301f4caedSMartin Mares { 'S','N', F_TEXT, "Serial number" },
4401f4caedSMartin Mares { 'Y','A', F_TEXT, "Asset tag" },
4501f4caedSMartin Mares { 'V', 0 , F_TEXT, "Vendor specific" },
4601f4caedSMartin Mares { 'Y', 0 , F_TEXT, "System specific" },
47d9b702cdSMartin Mares /* Non-standard extensions */
48eff7cc9eSreturn.0 { 'C','C', F_TEXT, "CCIN" },
49eff7cc9eSreturn.0 { 'F','C', F_TEXT, "Feature code" },
50eff7cc9eSreturn.0 { 'F','N', F_TEXT, "FRU" },
51eff7cc9eSreturn.0 { 'N','A', F_TEXT, "Network address" },
52eff7cc9eSreturn.0 { 'R','M', F_TEXT, "Firmware version" },
53eff7cc9eSreturn.0 { 'Z', 0 , F_TEXT, "Product specific" },
547f9d3023SBen Hutchings { 0, 0 , F_BINARY, "Unknown" }
5501f4caedSMartin Mares };
5601f4caedSMartin Mares
57c7a34993SMartin Mares static void
print_vpd_string(const byte * buf,word len)58c7a34993SMartin Mares print_vpd_string(const byte *buf, word len)
59c7a34993SMartin Mares {
60c7a34993SMartin Mares while (len--)
61c7a34993SMartin Mares {
62c7a34993SMartin Mares byte ch = *buf++;
63c7a34993SMartin Mares if (ch == '\\')
64c7a34993SMartin Mares printf("\\\\");
65db2a16f4SMartin Mares else if (!ch && !len)
66db2a16f4SMartin Mares ; /* Cards with null-terminated strings have been observed */
67c7a34993SMartin Mares else if (ch < 32 || ch == 127)
68c7a34993SMartin Mares printf("\\x%02x", ch);
69c7a34993SMartin Mares else
70c7a34993SMartin Mares putchar(ch);
71c7a34993SMartin Mares }
72c7a34993SMartin Mares }
73c7a34993SMartin Mares
7401f4caedSMartin Mares static void
print_vpd_binary(const byte * buf,word len)7501f4caedSMartin Mares print_vpd_binary(const byte *buf, word len)
7601f4caedSMartin Mares {
7701f4caedSMartin Mares int i;
7801f4caedSMartin Mares for (i = 0; i < len; i++)
7901f4caedSMartin Mares {
8001f4caedSMartin Mares if (i)
8101f4caedSMartin Mares putchar(' ');
8201f4caedSMartin Mares printf("%02x", buf[i]);
8301f4caedSMartin Mares }
8401f4caedSMartin Mares }
8501f4caedSMartin Mares
86c7a34993SMartin Mares static int
read_vpd(struct device * d,int pos,byte * buf,int len,byte * csum)87c7a34993SMartin Mares read_vpd(struct device *d, int pos, byte *buf, int len, byte *csum)
88c7a34993SMartin Mares {
89c7a34993SMartin Mares if (!pci_read_vpd(d->dev, pos, buf, len))
90c7a34993SMartin Mares return 0;
91c7a34993SMartin Mares while (len--)
92c7a34993SMartin Mares *csum += *buf++;
93c7a34993SMartin Mares return 1;
94c7a34993SMartin Mares }
95c7a34993SMartin Mares
96c7a34993SMartin Mares void
cap_vpd(struct device * d)97c7a34993SMartin Mares cap_vpd(struct device *d)
98c7a34993SMartin Mares {
99c7a34993SMartin Mares word res_addr = 0, res_len, part_pos, part_len;
10001f4caedSMartin Mares byte buf[256];
101c7a34993SMartin Mares byte tag;
102c7a34993SMartin Mares byte csum = 0;
103c7a34993SMartin Mares
104c7a34993SMartin Mares printf("Vital Product Data\n");
105746c9057SMartin Mares if (verbose < 2)
106746c9057SMartin Mares return;
107c7a34993SMartin Mares
108c7a34993SMartin Mares while (res_addr <= PCI_VPD_ADDR_MASK)
109c7a34993SMartin Mares {
110c7a34993SMartin Mares if (!read_vpd(d, res_addr, &tag, 1, &csum))
111c7a34993SMartin Mares break;
112c7a34993SMartin Mares if (tag & 0x80)
113c7a34993SMartin Mares {
114c7a34993SMartin Mares if (res_addr > PCI_VPD_ADDR_MASK + 1 - 3)
115c7a34993SMartin Mares break;
116c7a34993SMartin Mares if (!read_vpd(d, res_addr + 1, buf, 2, &csum))
117c7a34993SMartin Mares break;
118c7a34993SMartin Mares res_len = buf[0] + (buf[1] << 8);
119c7a34993SMartin Mares res_addr += 3;
120c7a34993SMartin Mares }
121c7a34993SMartin Mares else
122c7a34993SMartin Mares {
123c7a34993SMartin Mares res_len = tag & 7;
124c7a34993SMartin Mares tag >>= 3;
125c7a34993SMartin Mares res_addr += 1;
126c7a34993SMartin Mares }
127c7a34993SMartin Mares if (res_len > PCI_VPD_ADDR_MASK + 1 - res_addr)
128c7a34993SMartin Mares break;
129c7a34993SMartin Mares
130c7a34993SMartin Mares part_pos = 0;
131c7a34993SMartin Mares
132c7a34993SMartin Mares switch (tag)
133c7a34993SMartin Mares {
134c7a34993SMartin Mares case 0x0f:
135c7a34993SMartin Mares printf("\t\tEnd\n");
136c7a34993SMartin Mares return;
137c7a34993SMartin Mares
138c7a34993SMartin Mares case 0x82:
139c7a34993SMartin Mares printf("\t\tProduct Name: ");
140c7a34993SMartin Mares while (part_pos < res_len)
141c7a34993SMartin Mares {
142c7a34993SMartin Mares part_len = res_len - part_pos;
143c7a34993SMartin Mares if (part_len > sizeof(buf))
144c7a34993SMartin Mares part_len = sizeof(buf);
145c7a34993SMartin Mares if (!read_vpd(d, res_addr + part_pos, buf, part_len, &csum))
146c7a34993SMartin Mares break;
147c7a34993SMartin Mares print_vpd_string(buf, part_len);
148c7a34993SMartin Mares part_pos += part_len;
149c7a34993SMartin Mares }
150c7a34993SMartin Mares printf("\n");
151c7a34993SMartin Mares break;
152c7a34993SMartin Mares
153c7a34993SMartin Mares case 0x90:
154c7a34993SMartin Mares case 0x91:
155c7a34993SMartin Mares printf("\t\t%s fields:\n",
156c7a34993SMartin Mares (tag == 0x90) ? "Read-only" : "Read/write");
157c7a34993SMartin Mares
158c7a34993SMartin Mares while (part_pos + 3 <= res_len)
159c7a34993SMartin Mares {
160c7a34993SMartin Mares word read_len;
16101f4caedSMartin Mares const struct vpd_item *item;
162726b641bSMartin Mares byte id[2], id1, id2;
163c7a34993SMartin Mares
164c7a34993SMartin Mares if (!read_vpd(d, res_addr + part_pos, buf, 3, &csum))
165c7a34993SMartin Mares break;
166c7a34993SMartin Mares part_pos += 3;
167726b641bSMartin Mares memcpy(id, buf, 2);
168726b641bSMartin Mares id1 = id[0];
169726b641bSMartin Mares id2 = id[1];
170c7a34993SMartin Mares part_len = buf[2];
171c7a34993SMartin Mares if (part_len > res_len - part_pos)
172c7a34993SMartin Mares break;
173c7a34993SMartin Mares
17401f4caedSMartin Mares /* Is this item known? */
1757f9d3023SBen Hutchings for (item=vpd_items; item->id1 && item->id1 != id1 ||
1767f9d3023SBen Hutchings item->id2 && item->id2 != id2; item++)
17701f4caedSMartin Mares ;
17801f4caedSMartin Mares
179c7a34993SMartin Mares /* Only read the first byte of the RV field because the
180c7a34993SMartin Mares * remaining bytes are not included in the checksum. */
18101f4caedSMartin Mares read_len = (item->format == F_RESVD) ? 1 : part_len;
182c7a34993SMartin Mares if (!read_vpd(d, res_addr + part_pos, buf, read_len, &csum))
183c7a34993SMartin Mares break;
184c7a34993SMartin Mares
185726b641bSMartin Mares printf("\t\t\t[");
186726b641bSMartin Mares print_vpd_string(id, 2);
187726b641bSMartin Mares printf("] %s: ", item->name);
18801f4caedSMartin Mares
18901f4caedSMartin Mares switch (item->format)
190c7a34993SMartin Mares {
19101f4caedSMartin Mares case F_TEXT:
192c7a34993SMartin Mares print_vpd_string(buf, part_len);
193c7a34993SMartin Mares printf("\n");
19401f4caedSMartin Mares break;
19501f4caedSMartin Mares case F_BINARY:
19601f4caedSMartin Mares print_vpd_binary(buf, part_len);
197c7a34993SMartin Mares printf("\n");
19801f4caedSMartin Mares break;
19901f4caedSMartin Mares case F_RESVD:
20001f4caedSMartin Mares printf("checksum %s, %d byte(s) reserved\n", csum ? "bad" : "good", part_len - 1);
20101f4caedSMartin Mares break;
20201f4caedSMartin Mares case F_RDWR:
20301f4caedSMartin Mares printf("%d byte(s) free\n", part_len);
20401f4caedSMartin Mares break;
205c7a34993SMartin Mares }
206c7a34993SMartin Mares
207c7a34993SMartin Mares part_pos += part_len;
208c7a34993SMartin Mares }
209c7a34993SMartin Mares break;
210c7a34993SMartin Mares
211c7a34993SMartin Mares default:
212169bfd45SMartin Mares printf("\t\tUnknown %s resource type %02x, will not decode more.\n",
213c7a34993SMartin Mares (tag & 0x80) ? "large" : "small", tag & ~0x80);
2149eaaf5c7SMatthew Wilcox return;
215c7a34993SMartin Mares }
216c7a34993SMartin Mares
217c7a34993SMartin Mares res_addr += res_len;
218c7a34993SMartin Mares }
219c7a34993SMartin Mares
220c7a34993SMartin Mares if (res_addr == 0)
221c7a34993SMartin Mares printf("\t\tNot readable\n");
222c7a34993SMartin Mares else
223c7a34993SMartin Mares printf("\t\tNo end tag found\n");
224c7a34993SMartin Mares }
225