Re: [PATCH 3/5] PCI/VPD: Consolidate missing EEPROM checks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, Jul 16, 2021 at 12:16:55AM +0200, Heiner Kallweit wrote:
> On 15.07.2021 23:59, Bjorn Helgaas wrote:
> > From: Bjorn Helgaas <bhelgaas@xxxxxxxxxx>
> > 
> > A missing VPD EEPROM typically reads as either all 0xff or all zeroes.
> > Both cases lead to invalid VPD resource items.  A 0xff tag would be a Large
> > Resource with length 0xffff (65535).  That's invalid because VPD can only
> > be 32768 bytes, limited by the size of the address register in the VPD
> > Capability.
> > 
> > A VPD that reads as all zeroes is also invalid because a 0x00 tag is a
> > Small Resource with length 0, which would result in an item of length 1.
> > This isn't explicitly illegal in PCIe r5.0, sec 6.28, but the format is
> > derived from PNP ISA, which *does* say "a small resource data type may be
> > 2-8 bytes in size" (Plug and Play ISA v1.0a, sec 6.2.2.
> > 
> > Check for these invalid tags and return VPD size of zero if we find them.
> > If they occur at the beginning of VPD, assume it's the result of a missing
> > EEPROM.
> > 
> > Signed-off-by: Bjorn Helgaas <bhelgaas@xxxxxxxxxx>
> > ---
> >  drivers/pci/vpd.c | 36 +++++++++++++++++++++++++++---------
> >  1 file changed, 27 insertions(+), 9 deletions(-)
> > 
> > diff --git a/drivers/pci/vpd.c b/drivers/pci/vpd.c
> > index 9b54dd95e42c..9c2744d79b53 100644
> > --- a/drivers/pci/vpd.c
> > +++ b/drivers/pci/vpd.c
> > @@ -77,11 +77,7 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)
> >  
> >  	while (off < old_size && pci_read_vpd(dev, off, 1, header) == 1) {
> >  		unsigned char tag;
> > -
> > -		if (!header[0] && !off) {
> > -			pci_info(dev, "Invalid VPD tag 00, assume missing optional VPD EPROM\n");
> > -			return 0;
> > -		}
> > +		size_t size;
> >  
> >  		if (header[0] & PCI_VPD_LRDT) {
> >  			/* Large Resource Data Type Tag */
> > @@ -96,8 +92,16 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)
> >  						 off + 1);
> >  					return 0;
> >  				}
> > -				off += PCI_VPD_LRDT_TAG_SIZE +
> > -					pci_vpd_lrdt_size(header);
> > +				size = pci_vpd_lrdt_size(header);
> > +
> > +				/*
> > +				 * Missing EEPROM may read as 0xff.
> > +				 * Length of 0xffff (65535) cannot be valid
> > +				 * because VPD can't be that large.
> > +				 */
> 
> I'm not fully convinced. Instead of checking for a "no VPD EPROM" read (00/ff)
> directly, we now do it indirectly based on the internal tag structure.
> We have pci_vpd_lrdt_size() et al to encapsulate the internal structure.
> IMO the code is harder to understand now.

I don't quite follow.  Previously we checked for 0x00 data
("if (!header[0] && !off)"), but we didn't check directly for 0xff.

If we read 0xff, we took the PCI_VPD_LRDT case, but it wouldn't match
ID_STRING, RO_DATA, or RW_DATA, so we'd fall out and check again
against ID_STRING, RO_DATA, and RW_DATA, and take the "invalid
%s VPD tag" error path because it doesn't match any.

This results in failure for any large resource except ID_STRING,
RO_DATA, and RW_DATA, regardless of the size.

My proposed code catches a different set of invalid things.
"size > PCI_VPD_MAX_SIZE" will catch any large resource headers with
length 0x8001 through 0xffff.

Possibly it should actually check for "off + size > PCI_VPD_MAX_SIZE"
so e.g., it would catch a 0x20 byte resource starting at 0x7ff0.

> > +				if (size > PCI_VPD_MAX_SIZE)
> > +					goto error;
> > +				off += PCI_VPD_LRDT_TAG_SIZE + size;
> >  			} else {
> >  				pci_warn(dev, "invalid large VPD tag %02x at offset %zu",
> >  					 tag, off);
> > @@ -105,14 +109,28 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)
> >  			}
> >  		} else {
> >  			/* Short Resource Data Type Tag */
> > -			off += PCI_VPD_SRDT_TAG_SIZE +
> > -				pci_vpd_srdt_size(header);
> >  			tag = pci_vpd_srdt_tag(header);
> > +			size = pci_vpd_srdt_size(header);
> > +
> > +			/*
> > +			 * Missing EEPROM may read as 0x00.  A small item
> > +			 * must be at least 2 bytes.
> > +			 */
> > +			if (size == 0)
> > +				goto error;
> > +
> > +			off += PCI_VPD_SRDT_TAG_SIZE + size;
> >  			if (tag == PCI_VPD_STIN_END)	/* End tag descriptor */
> >  				return off;
> >  		}
> >  	}
> >  	return 0;
> > +
> > +error:
> > +	pci_info(dev, "invalid VPD tag %#04x at offset %zu%s\n",
> > +		 header[0], off, off == 0 ?
> > +		 "; assume missing optional EEPROM" : "");
> > +	return 0;
> >  }
> >  
> >  /*
> > 
> 



[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux