cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

amdgpu_fru_eeprom.c (6451B)


      1/*
      2 * Copyright 2019 Advanced Micro Devices, Inc.
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining a
      5 * copy of this software and associated documentation files (the "Software"),
      6 * to deal in the Software without restriction, including without limitation
      7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8 * and/or sell copies of the Software, and to permit persons to whom the
      9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice shall be included in
     12 * all copies or substantial portions of the Software.
     13 *
     14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     20 * OTHER DEALINGS IN THE SOFTWARE.
     21 *
     22 */
     23#include <linux/pci.h>
     24
     25#include "amdgpu.h"
     26#include "amdgpu_i2c.h"
     27#include "smu_v11_0_i2c.h"
     28#include "atom.h"
     29#include "amdgpu_fru_eeprom.h"
     30#include "amdgpu_eeprom.h"
     31
     32#define FRU_EEPROM_MADDR        0x60000
     33
     34static bool is_fru_eeprom_supported(struct amdgpu_device *adev)
     35{
     36	/* Only server cards have the FRU EEPROM
     37	 * TODO: See if we can figure this out dynamically instead of
     38	 * having to parse VBIOS versions.
     39	 */
     40	struct atom_context *atom_ctx = adev->mode_info.atom_context;
     41
     42	/* The i2c access is blocked on VF
     43	 * TODO: Need other way to get the info
     44	 */
     45	if (amdgpu_sriov_vf(adev))
     46		return false;
     47
     48	/* VBIOS is of the format ###-DXXXYYYY-##. For SKU identification,
     49	 * we can use just the "DXXX" portion. If there were more models, we
     50	 * could convert the 3 characters to a hex integer and use a switch
     51	 * for ease/speed/readability. For now, 2 string comparisons are
     52	 * reasonable and not too expensive
     53	 */
     54	switch (adev->asic_type) {
     55	case CHIP_VEGA20:
     56		/* D161 and D163 are the VG20 server SKUs */
     57		if (strnstr(atom_ctx->vbios_version, "D161",
     58			    sizeof(atom_ctx->vbios_version)) ||
     59		    strnstr(atom_ctx->vbios_version, "D163",
     60			    sizeof(atom_ctx->vbios_version)))
     61			return true;
     62		else
     63			return false;
     64	case CHIP_ALDEBARAN:
     65		/* All Aldebaran SKUs have the FRU */
     66		return true;
     67	case CHIP_SIENNA_CICHLID:
     68		if (strnstr(atom_ctx->vbios_version, "D603",
     69			    sizeof(atom_ctx->vbios_version)))
     70			return true;
     71		else
     72			return false;
     73	default:
     74		return false;
     75	}
     76}
     77
     78static int amdgpu_fru_read_eeprom(struct amdgpu_device *adev, uint32_t addrptr,
     79				  unsigned char *buf, size_t buf_size)
     80{
     81	int ret;
     82	u8 size;
     83
     84	ret = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, addrptr, buf, 1);
     85	if (ret < 1) {
     86		DRM_WARN("FRU: Failed to get size field");
     87		return ret;
     88	}
     89
     90	/* The size returned by the i2c requires subtraction of 0xC0 since the
     91	 * size apparently always reports as 0xC0+actual size.
     92	 */
     93	size = buf[0] & 0x3F;
     94	size = min_t(size_t, size, buf_size);
     95
     96	ret = amdgpu_eeprom_read(adev->pm.fru_eeprom_i2c_bus, addrptr + 1,
     97				 buf, size);
     98	if (ret < 1) {
     99		DRM_WARN("FRU: Failed to get data field");
    100		return ret;
    101	}
    102
    103	return size;
    104}
    105
    106int amdgpu_fru_get_product_info(struct amdgpu_device *adev)
    107{
    108	unsigned char buf[AMDGPU_PRODUCT_NAME_LEN];
    109	u32 addrptr;
    110	int size, len;
    111
    112	if (!is_fru_eeprom_supported(adev))
    113		return 0;
    114
    115	/* If algo exists, it means that the i2c_adapter's initialized */
    116	if (!adev->pm.fru_eeprom_i2c_bus || !adev->pm.fru_eeprom_i2c_bus->algo) {
    117		DRM_WARN("Cannot access FRU, EEPROM accessor not initialized");
    118		return -ENODEV;
    119	}
    120
    121	/* There's a lot of repetition here. This is due to the FRU having
    122	 * variable-length fields. To get the information, we have to find the
    123	 * size of each field, and then keep reading along and reading along
    124	 * until we get all of the data that we want. We use addrptr to track
    125	 * the address as we go
    126	 */
    127
    128	/* The first fields are all of size 1-byte, from 0-7 are offsets that
    129	 * contain information that isn't useful to us.
    130	 * Bytes 8-a are all 1-byte and refer to the size of the entire struct,
    131	 * and the language field, so just start from 0xb, manufacturer size
    132	 */
    133	addrptr = FRU_EEPROM_MADDR + 0xb;
    134	size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
    135	if (size < 1) {
    136		DRM_ERROR("Failed to read FRU Manufacturer, ret:%d", size);
    137		return -EINVAL;
    138	}
    139
    140	/* Increment the addrptr by the size of the field, and 1 due to the
    141	 * size field being 1 byte. This pattern continues below.
    142	 */
    143	addrptr += size + 1;
    144	size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
    145	if (size < 1) {
    146		DRM_ERROR("Failed to read FRU product name, ret:%d", size);
    147		return -EINVAL;
    148	}
    149
    150	len = size;
    151	if (len >= AMDGPU_PRODUCT_NAME_LEN) {
    152		DRM_WARN("FRU Product Name is larger than %d characters. This is likely a mistake",
    153				AMDGPU_PRODUCT_NAME_LEN);
    154		len = AMDGPU_PRODUCT_NAME_LEN - 1;
    155	}
    156	memcpy(adev->product_name, buf, len);
    157	adev->product_name[len] = '\0';
    158
    159	addrptr += size + 1;
    160	size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
    161	if (size < 1) {
    162		DRM_ERROR("Failed to read FRU product number, ret:%d", size);
    163		return -EINVAL;
    164	}
    165
    166	len = size;
    167	/* Product number should only be 16 characters. Any more,
    168	 * and something could be wrong. Cap it at 16 to be safe
    169	 */
    170	if (len >= sizeof(adev->product_number)) {
    171		DRM_WARN("FRU Product Number is larger than 16 characters. This is likely a mistake");
    172		len = sizeof(adev->product_number) - 1;
    173	}
    174	memcpy(adev->product_number, buf, len);
    175	adev->product_number[len] = '\0';
    176
    177	addrptr += size + 1;
    178	size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
    179
    180	if (size < 1) {
    181		DRM_ERROR("Failed to read FRU product version, ret:%d", size);
    182		return -EINVAL;
    183	}
    184
    185	addrptr += size + 1;
    186	size = amdgpu_fru_read_eeprom(adev, addrptr, buf, sizeof(buf));
    187
    188	if (size < 1) {
    189		DRM_ERROR("Failed to read FRU serial number, ret:%d", size);
    190		return -EINVAL;
    191	}
    192
    193	len = size;
    194	/* Serial number should only be 16 characters. Any more,
    195	 * and something could be wrong. Cap it at 16 to be safe
    196	 */
    197	if (len >= sizeof(adev->serial)) {
    198		DRM_WARN("FRU Serial Number is larger than 16 characters. This is likely a mistake");
    199		len = sizeof(adev->serial) - 1;
    200	}
    201	memcpy(adev->serial, buf, len);
    202	adev->serial[len] = '\0';
    203
    204	return 0;
    205}