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