[PATCH 3/4] drm/amdgpu: Read IPMI data for product information
Russell, Kent
Kent.Russell at amd.com
Wed Nov 16 21:19:05 UTC 2022
[AMD Official Use Only - General]
You can add my
Reviewed-by: Kent Russell <kent.russell at amd.com>
as well. And I have no issue with a little elaboration on the commit. We definitely changed a few things here.
Kent
> -----Original Message-----
> From: Alex Deucher <alexdeucher at gmail.com>
> Sent: Wednesday, November 16, 2022 3:59 PM
> To: Tuikov, Luben <Luben.Tuikov at amd.com>
> Cc: AMD Graphics <amd-gfx at lists.freedesktop.org>; Deucher, Alexander
> <Alexander.Deucher at amd.com>; Russell, Kent <Kent.Russell at amd.com>
> Subject: Re: [PATCH 3/4] drm/amdgpu: Read IPMI data for product information
>
> On Wed, Nov 16, 2022 at 2:49 PM Luben Tuikov <luben.tuikov at amd.com>
> wrote:
> >
> > Read and interpret IPMI data to get the product name, product model, and
> > product serial number.
>
> Patches 1,2,4 are:
> Reviewed-by: Alex Deucher <alexander.deucher at amd.com>
> for patch 3:
> It's not clear from the commit message what this change is doing. Is
> this just a rewrite/cleanup of the existing FRU parsing or is there a
> bug fix in here related to the rest of this series?
>
> Alex
>
> >
> > Cc: Alex Deucher <Alexander.Deucher at amd.com>
> > Cc: Kent Russell <kent.russell at amd.com>
> > Signed-off-by: Luben Tuikov <luben.tuikov at amd.com>
> > Tested-by: Kent Russell <kent.russell at amd.com>
> > ---
> > .../gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c | 183 ++++++++----------
> > 1 file changed, 85 insertions(+), 98 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
> b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
> > index 9b2ff386b7c4f8..2c38ac7bc643d5 100644
> > --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
> > +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c
> > @@ -94,39 +94,12 @@ static bool is_fru_eeprom_supported(struct
> amdgpu_device *adev, u32 *fru_addr)
> > }
> > }
> >
> > -static int amdgpu_fru_read_eeprom(struct amdgpu_device *adev, uint32_t
> addrptr,
> > - unsigned char *buf, size_t buf_size)
> > -{
> > - int ret;
> > - u8 size;
> > -
> > - ret = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, addrptr, buf,
> 1);
> > - if (ret < 1) {
> > - DRM_WARN("FRU: Failed to get size field");
> > - return ret;
> > - }
> > -
> > - /* The size returned by the i2c requires subtraction of 0xC0 since the
> > - * size apparently always reports as 0xC0+actual size.
> > - */
> > - size = buf[0] & 0x3F;
> > - size = min_t(size_t, size, buf_size);
> > -
> > - ret = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, addrptr + 1,
> > - buf, size);
> > - if (ret < 1) {
> > - DRM_WARN("FRU: Failed to get data field");
> > - return ret;
> > - }
> > -
> > - return size;
> > -}
> > -
> > int amdgpu_fru_get_product_info(struct amdgpu_device *adev)
> > {
> > - unsigned char buf[AMDGPU_PRODUCT_NAME_LEN];
> > - u32 addrptr, fru_addr;
> > + unsigned char buf[8], *pia;
> > + u32 addr, fru_addr;
> > int size, len;
> > + u8 csum;
> >
> > if (!is_fru_eeprom_supported(adev, &fru_addr))
> > return 0;
> > @@ -137,88 +110,102 @@ int amdgpu_fru_get_product_info(struct
> amdgpu_device *adev)
> > return -ENODEV;
> > }
> >
> > - /* There's a lot of repetition here. This is due to the FRU having
> > - * variable-length fields. To get the information, we have to find the
> > - * size of each field, and then keep reading along and reading along
> > - * until we get all of the data that we want. We use addrptr to track
> > - * the address as we go
> > - */
> > -
> > - /* The first fields are all of size 1-byte, from 0-7 are offsets that
> > - * contain information that isn't useful to us.
> > - * Bytes 8-a are all 1-byte and refer to the size of the entire struct,
> > - * and the language field, so just start from 0xb, manufacturer size
> > - */
> > - addrptr = fru_addr + 0xb;
> > - size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
> > - if (size < 1) {
> > - DRM_ERROR("Failed to read FRU Manufacturer, ret:%d", size);
> > - return -EINVAL;
> > + /* Read the IPMI Common header */
> > + len = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, fru_addr,
> buf,
> > + sizeof(buf));
> > + if (len != 8) {
> > + DRM_ERROR("Couldn't read the IPMI Common Header: %d", len);
> > + return len < 0 ? len : -EIO;
> > }
> >
> > - /* Increment the addrptr by the size of the field, and 1 due to the
> > - * size field being 1 byte. This pattern continues below.
> > - */
> > - addrptr += size + 1;
> > - size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
> > - if (size < 1) {
> > - DRM_ERROR("Failed to read FRU product name, ret:%d", size);
> > - return -EINVAL;
> > + if (buf[0] != 1) {
> > + DRM_ERROR("Bad IPMI Common Header version: 0x%02x", buf[0]);
> > + return -EIO;
> > }
> >
> > - len = size;
> > - if (len >= AMDGPU_PRODUCT_NAME_LEN) {
> > - DRM_WARN("FRU Product Name is larger than %d characters. This is
> likely a mistake",
> > - AMDGPU_PRODUCT_NAME_LEN);
> > - len = AMDGPU_PRODUCT_NAME_LEN - 1;
> > - }
> > - memcpy(adev->product_name, buf, len);
> > - adev->product_name[len] = '\0';
> > -
> > - addrptr += size + 1;
> > - size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
> > - if (size < 1) {
> > - DRM_ERROR("Failed to read FRU product number, ret:%d", size);
> > - return -EINVAL;
> > + for (csum = 0; len > 0; len--)
> > + csum += buf[len - 1];
> > + if (csum) {
> > + DRM_ERROR("Bad IPMI Common Header checksum: 0x%02x",
> csum);
> > + return -EIO;
> > }
> >
> > - len = size;
> > - /* Product number should only be 16 characters. Any more,
> > - * and something could be wrong. Cap it at 16 to be safe
> > - */
> > - if (len >= sizeof(adev->product_number)) {
> > - DRM_WARN("FRU Product Number is larger than 16 characters. This
> is likely a mistake");
> > - len = sizeof(adev->product_number) - 1;
> > - }
> > - memcpy(adev->product_number, buf, len);
> > - adev->product_number[len] = '\0';
> > + /* Get the offset to the Product Info Area (PIA). */
> > + addr = buf[4] * 8;
> > + if (!addr)
> > + return 0;
> >
> > - addrptr += size + 1;
> > - size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
> > + /* Get the absolute address to the PIA. */
> > + addr += fru_addr;
> >
> > - if (size < 1) {
> > - DRM_ERROR("Failed to read FRU product version, ret:%d", size);
> > - return -EINVAL;
> > + /* Read the header of the PIA. */
> > + len = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, addr, buf,
> 3);
> > + if (len != 3) {
> > + DRM_ERROR("Couldn't read the Product Info Area header: %d", len);
> > + return len < 0 ? len : -EIO;
> > }
> >
> > - addrptr += size + 1;
> > - size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
> > + if (buf[0] != 1) {
> > + DRM_ERROR("Bad IPMI Product Info Area version: 0x%02x", buf[0]);
> > + return -EIO;
> > + }
> >
> > - if (size < 1) {
> > - DRM_ERROR("Failed to read FRU serial number, ret:%d", size);
> > - return -EINVAL;
> > + size = buf[1] * 8;
> > + pia = kzalloc(size, GFP_KERNEL);
> > + if (!pia)
> > + return -ENOMEM;
> > +
> > + /* Read the whole PIA. */
> > + len = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, addr, pia,
> size);
> > + if (len != size) {
> > + kfree(pia);
> > + DRM_ERROR("Couldn't read the Product Info Area: %d", len);
> > + return len < 0 ? len : -EIO;
> > }
> >
> > - len = size;
> > - /* Serial number should only be 16 characters. Any more,
> > - * and something could be wrong. Cap it at 16 to be safe
> > - */
> > - if (len >= sizeof(adev->serial)) {
> > - DRM_WARN("FRU Serial Number is larger than 16 characters. This is
> likely a mistake");
> > - len = sizeof(adev->serial) - 1;
> > + for (csum = 0; size > 0; size--)
> > + csum += pia[size - 1];
> > + if (csum) {
> > + DRM_ERROR("Bad Product Info Area checksum: 0x%02x", csum);
> > + return -EIO;
> > }
> > - memcpy(adev->serial, buf, len);
> > - adev->serial[len] = '\0';
> >
> > + /* Now extract useful information from the PIA.
> > + *
> > + * Skip the Manufacturer Name at [3] and go directly to
> > + * the Product Name field.
> > + */
> > + addr = 3 + 1 + (pia[3] & 0x3F);
> > + if (addr + 1 >= len)
> > + goto Out;
> > + memcpy(adev->product_name, pia + addr + 1,
> > + min_t(size_t,
> > + sizeof(adev->product_name),
> > + pia[addr] & 0x3F));
> > + adev->product_name[sizeof(adev->product_name) - 1] = '\0';
> > +
> > + /* Go to the Product Part/Model Number field. */
> > + addr += 1 + (pia[addr] & 0x3F);
> > + if (addr + 1 >= len)
> > + goto Out;
> > + memcpy(adev->product_number, pia + addr + 1,
> > + min_t(size_t,
> > + sizeof(adev->product_number),
> > + pia[addr] & 0x3F));
> > + adev->product_number[sizeof(adev->product_number) - 1] = '\0';
> > +
> > + /* Go to the Product Version field. */
> > + addr += 1 + (pia[addr] & 0x3F);
> > +
> > + /* Go to the Product Serial Number field. */
> > + addr += 1 + (pia[addr] & 0x3F);
> > + if (addr + 1 >= len)
> > + goto Out;
> > + memcpy(adev->serial, pia + addr + 1, min_t(size_t,
> > + sizeof(adev->serial),
> > + pia[addr] & 0x3F));
> > + adev->serial[sizeof(adev->serial) - 1] = '\0';
> > +Out:
> > + kfree(pia);
> > return 0;
> > }
> > --
> > 2.38.1
> >
More information about the amd-gfx
mailing list