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