[PATCH 3/4] drm/amdgpu: Interpret IPMI data for product information (v2)

Alex Deucher alexdeucher at gmail.com
Thu Nov 17 03:37:34 UTC 2022


On Wed, Nov 16, 2022 at 8:25 PM Luben Tuikov <luben.tuikov at amd.com> wrote:
>
> Don't assume FRU MCU memory locations for the FRU data fields, or their sizes,
> instead reading and interpret the IPMI data, as stipulated in the IPMI spec
> version 1.0 rev 1.2.
>
> Extract the Product Name, Product Part/Model Number, and the Product Serial
> Number by interpreting the IPMI data.
>
> Check the checksums of the stored IPMI data to make sure we don't read and
> give corrupted data back the the user.
>
> Eliminate small I2C reads, and instead read the whole Product Info Area in one
> go, and then extract the information we're seeking from it.
>
> Eliminates a whole function, making this file smaller.
>
> v2: Clarify changes in the commit message.

Thanks for clarifying, Series is:
Reviewed-by: Alex Deucher <alexander.deucher at amd.com>

>
> 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>
> Reviewed-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