]> mj.ucw.cz Git - pciutils.git/blob - ls-vpd.c
Ignore NUL character at the end of VPD strings.
[pciutils.git] / ls-vpd.c
1 /*
2  *      The PCI Utilities -- Show Vital Product Data
3  *
4  *      Copyright (c) 2008 Solarflare Communications
5  *
6  *      Written by Ben Hutchings <bhutchings@solarflare.com>
7  *      Improved by Martin Mares <mj@ucw.cz>
8  *
9  *      Can be freely distributed and used under the terms of the GNU GPL.
10  */
11
12 #include <stdio.h>
13
14 #include "lspci.h"
15
16 /*
17  *  The list of all known VPD items and their formats.
18  *  Technically, this belongs to the pci.ids file, but the VPD does not seem
19  *  to be developed any longer, so we have chosen the easier way.
20  */
21
22 enum vpd_format {
23   F_BINARY,
24   F_TEXT,
25   F_RESVD,
26   F_RDWR,
27 };
28
29 static const struct vpd_item {
30   byte id1, id2;
31   byte format;
32   const char *name;
33 } vpd_items[] = {
34   { 'C','P', F_BINARY,  "Extended capability" },
35   { 'E','C', F_TEXT,    "Engineering changes" },
36   { 'M','N', F_BINARY,  "Manufacture ID" },
37   { 'P','N', F_TEXT,    "Part number" },
38   { 'R','V', F_RESVD,   "Reserved" },
39   { 'R','W', F_RDWR,    "Read-write area" },
40   { 'S','N', F_TEXT,    "Serial number" },
41   { 'Y','A', F_TEXT,    "Asset tag" },
42   { 'V', 0 , F_TEXT,    "Vendor specific" },
43   { 'Y', 0 , F_TEXT,    "System specific" },
44   {  0,  0 , F_BINARY,  NULL }
45 };
46
47 static void
48 print_vpd_string(const byte *buf, word len)
49 {
50   while (len--)
51     {
52       byte ch = *buf++;
53       if (ch == '\\')
54         printf("\\\\");
55       else if (!ch && !len)
56         ;  /* Cards with null-terminated strings have been observed */
57       else if (ch < 32 || ch == 127)
58         printf("\\x%02x", ch);
59       else
60         putchar(ch);
61     }
62 }
63
64 static void
65 print_vpd_binary(const byte *buf, word len)
66 {
67   int i;
68   for (i = 0; i < len; i++)
69     {
70       if (i)
71         putchar(' ');
72       printf("%02x", buf[i]);
73     }
74 }
75
76 static int
77 read_vpd(struct device *d, int pos, byte *buf, int len, byte *csum)
78 {
79   if (!pci_read_vpd(d->dev, pos, buf, len))
80     return 0;
81   while (len--)
82     *csum += *buf++;
83   return 1;
84 }
85
86 void
87 cap_vpd(struct device *d)
88 {
89   word res_addr = 0, res_len, part_pos, part_len;
90   byte buf[256];
91   byte tag;
92   byte csum = 0;
93
94   printf("Vital Product Data\n");
95   if (verbose < 2)
96     return;
97
98   while (res_addr <= PCI_VPD_ADDR_MASK)
99     {
100       if (!read_vpd(d, res_addr, &tag, 1, &csum))
101         break;
102       if (tag & 0x80)
103         {
104           if (res_addr > PCI_VPD_ADDR_MASK + 1 - 3)
105             break;
106           if (!read_vpd(d, res_addr + 1, buf, 2, &csum))
107             break;
108           res_len = buf[0] + (buf[1] << 8);
109           res_addr += 3;
110         }
111       else
112         {
113           res_len = tag & 7;
114           tag >>= 3;
115           res_addr += 1;
116         }
117       if (res_len > PCI_VPD_ADDR_MASK + 1 - res_addr)
118         break;
119
120       part_pos = 0;
121
122       switch (tag)
123         {
124         case 0x0f:
125           printf("\t\tEnd\n");
126           return;
127
128         case 0x82:
129           printf("\t\tProduct Name: ");
130           while (part_pos < res_len)
131             {
132               part_len = res_len - part_pos;
133               if (part_len > sizeof(buf))
134                 part_len = sizeof(buf);
135               if (!read_vpd(d, res_addr + part_pos, buf, part_len, &csum))
136                 break;
137               print_vpd_string(buf, part_len);
138               part_pos += part_len;
139             }
140           printf("\n");
141           break;
142
143         case 0x90:
144         case 0x91:
145           printf("\t\t%s fields:\n",
146                  (tag == 0x90) ? "Read-only" : "Read/write");
147
148           while (part_pos + 3 <= res_len)
149             {
150               word read_len;
151               const struct vpd_item *item;
152               struct vpd_item unknown_item;
153
154               if (!read_vpd(d, res_addr + part_pos, buf, 3, &csum))
155                 break;
156               part_pos += 3;
157               part_len = buf[2];
158               if (part_len > res_len - part_pos)
159                 break;
160
161               /* Is this item known? */
162               for (item=vpd_items; item->id1 && item->id1 != buf[0] ||
163                                    item->id2 && item->id2 != buf[1]; item++)
164                 ;
165               if (!item->id1 && !item->id2)
166                 {
167                   unknown_item.id1 = buf[0];
168                   unknown_item.id2 = buf[1];
169                   unknown_item.format = F_BINARY;
170                   unknown_item.name = "Unknown";
171                   item = &unknown_item;
172                 }
173
174               /* Only read the first byte of the RV field because the
175                * remaining bytes are not included in the checksum. */
176               read_len = (item->format == F_RESVD) ? 1 : part_len;
177               if (!read_vpd(d, res_addr + part_pos, buf, read_len, &csum))
178                 break;
179
180               printf("\t\t\t[%c%c] %s: ", item->id1, item->id2, item->name);
181
182               switch (item->format)
183                 {
184                 case F_TEXT:
185                   print_vpd_string(buf, part_len);
186                   printf("\n");
187                   break;
188                 case F_BINARY:
189                   print_vpd_binary(buf, part_len);
190                   printf("\n");
191                   break;
192                 case F_RESVD:
193                   printf("checksum %s, %d byte(s) reserved\n", csum ? "bad" : "good", part_len - 1);
194                   break;
195                 case F_RDWR:
196                   printf("%d byte(s) free\n", part_len);
197                   break;
198                 }
199
200               part_pos += part_len;
201             }
202           break;
203
204         default:
205           printf("\t\tUnknown %s resource type %02x\n",
206                  (tag & 0x80) ? "large" : "small", tag & ~0x80);
207           break;
208         }
209
210       res_addr += res_len;
211     }
212
213   if (res_addr == 0)
214     printf("\t\tNot readable\n");
215   else
216     printf("\t\tNo end tag found\n");
217 }