169408Sache// SPDX-License-Identifier: GPL-2.0+ 269408Sache/* 3316958Sdchagin * Copyright 2016 General Electric Company 4100616Smp */ 5316958Sdchagin 6100616Smp#include "vpd_reader.h" 769408Sache#include <malloc.h> 869408Sache 9316958Sdchagin#include <i2c.h> 1069408Sache#include <linux/bch.h> 1169408Sache#include <stdlib.h> 1269408Sache#include <dm/uclass.h> 1369408Sache#include <i2c_eeprom.h> 1469408Sache#include <hexdump.h> 1569408Sache 1669408Sache/* BCH configuration */ 1769408Sache 18316958Sdchaginconst struct { 1969408Sache int header_ecc_capability_bits; 2069408Sache int data_ecc_capability_bits; 2169408Sache unsigned int prim_poly; 2269408Sache struct { 23316958Sdchagin int min; 24316958Sdchagin int max; 25316958Sdchagin } galois_field_order; 2669408Sache} bch_configuration = { 27316958Sdchagin .header_ecc_capability_bits = 4, 2869408Sache .data_ecc_capability_bits = 16, 29231990Smp .prim_poly = 0, 30231990Smp .galois_field_order = { 31316958Sdchagin .min = 5, 32316958Sdchagin .max = 15, 3369408Sache }, 34316958Sdchagin}; 35100616Smp 36100616Smpstatic int calculate_galois_field_order(size_t source_length) 37100616Smp{ 38100616Smp int gfo = bch_configuration.galois_field_order.min; 39100616Smp 40100616Smp for (; gfo < bch_configuration.galois_field_order.max && 41100616Smp ((((1 << gfo) - 1) - ((int)source_length * 8)) < 0); 42100616Smp gfo++) { 43100616Smp } 44100616Smp 45100616Smp if (gfo == bch_configuration.galois_field_order.max) 46100616Smp return -1; 47100616Smp 48100616Smp return gfo + 1; 49100616Smp} 50100616Smp 51100616Smpstatic int verify_bch(int ecc_bits, unsigned int prim_poly, u8 *data, 52100616Smp size_t data_length, const u8 *ecc, size_t ecc_length) 53316958Sdchagin{ 54100616Smp int gfo = calculate_galois_field_order(data_length); 55100616Smp 56100616Smp if (gfo < 0) 57100616Smp return -1; 58100616Smp 59100616Smp struct bch_control *bch = init_bch(gfo, ecc_bits, prim_poly); 60100616Smp 61100616Smp if (!bch) 62100616Smp return -1; 63100616Smp 64100616Smp if (bch->ecc_bytes != ecc_length) { 65195609Smp free_bch(bch); 66100616Smp return -1; 67195609Smp } 68100616Smp 69195609Smp unsigned int *errloc = (unsigned int *)calloc(data_length, 70100616Smp sizeof(unsigned int)); 71100616Smp int errors = decode_bch(bch, data, data_length, ecc, NULL, NULL, 72100616Smp errloc); 73100616Smp 74100616Smp free_bch(bch); 75100616Smp if (errors < 0) { 76100616Smp free(errloc); 77100616Smp return -1; 78100616Smp } 79100616Smp 80100616Smp if (errors > 0) { 81100616Smp for (int n = 0; n < errors; n++) { 82100616Smp if (errloc[n] >= 8 * data_length) { 83100616Smp /* 84100616Smp * n-th error located in ecc (no need for data 8583098Smp * correction) 8683098Smp */ 87100616Smp } else { 8883098Smp /* n-th error located in data */ 89100616Smp data[errloc[n] / 8] ^= 1 << (errloc[n] % 8); 90100616Smp } 91100616Smp } 92100616Smp } 93100616Smp 94100616Smp free(errloc); 95100616Smp return 0; 96100616Smp} 97195609Smp 98100616Smpstatic const int ID; 99195609Smpstatic const int LEN = 1; 100195609Smpstatic const int VER = 2; 101195609Smpstatic const int TYP = 3; 102195609Smpstatic const int BLOCK_SIZE = 4; 103195609Smp 104195609Smpstatic const u8 HEADER_BLOCK_ID; 105195609Smpstatic const u8 HEADER_BLOCK_LEN = 18; 106195609Smpstatic const u32 HEADER_BLOCK_MAGIC = 0xca53ca53; 107195609Smpstatic const size_t HEADER_BLOCK_VERIFY_LEN = 14; 108195609Smpstatic const size_t HEADER_BLOCK_ECC_OFF = 14; 109100616Smpstatic const size_t HEADER_BLOCK_ECC_LEN = 4; 110100616Smp 111100616Smpstatic const u8 ECC_BLOCK_ID = 0xFF; 112195609Smp 113100616Smpint vpd_reader(size_t size, u8 *data, struct vpd_cache *userdata, 114100616Smp int (*fn)(struct vpd_cache *, u8 id, u8 version, u8 type, 115100616Smp size_t size, u8 const *data)) 116100616Smp{ 117100616Smp if (size < HEADER_BLOCK_LEN || !data || !fn) 118100616Smp return -EINVAL; 119100616Smp 120100616Smp /* 121100616Smp * +--------------------+----------------+--//--+--------------------+ 122195609Smp * | header block | data block | ... | ecc block | 123100616Smp * +--------------------+----------------+--//--+--------------------+ 12469408Sache * : : : 125100616Smp * +------+-------+-----+ +------+-------------+ 12669408Sache * | id | magic | ecc | | ... | ecc | 12769408Sache * | len | off | | +------+-------------+ 12869408Sache * | ver | size | | : 12969408Sache * | type | | | : 13069408Sache * +------+-------+-----+ : 13169408Sache * : : : : 132100616Smp * <----- [1] ----> <--------- [2] ---------> 13369408Sache * 13469408Sache * Repair (if necessary) the contents of header block [1] by using a 135316958Sdchagin * 4 byte ECC located at the end of the header block. A successful 136316958Sdchagin * return value means that we can trust the header. 137316958Sdchagin */ 138316958Sdchagin int ret = verify_bch(bch_configuration.header_ecc_capability_bits, 139316958Sdchagin bch_configuration.prim_poly, data, 140316958Sdchagin HEADER_BLOCK_VERIFY_LEN, 141316958Sdchagin &data[HEADER_BLOCK_ECC_OFF], HEADER_BLOCK_ECC_LEN); 142316958Sdchagin if (ret < 0) 143316958Sdchagin return ret; 144316958Sdchagin 145316958Sdchagin /* Validate header block { id, length, version, type }. */ 146316958Sdchagin if (data[ID] != HEADER_BLOCK_ID || data[LEN] != HEADER_BLOCK_LEN || 147316958Sdchagin data[VER] != 0 || data[TYP] != 0 || 148316958Sdchagin ntohl(*(u32 *)(&data[4])) != HEADER_BLOCK_MAGIC) 149316958Sdchagin return -EINVAL; 150316958Sdchagin 151316958Sdchagin u32 offset = ntohl(*(u32 *)(&data[8])); 152316958Sdchagin u16 size_bits = ntohs(*(u16 *)(&data[12])); 153316958Sdchagin 154316958Sdchagin /* Check that ECC header fits. */ 155316958Sdchagin if (offset + 3 >= size) 15669408Sache return -EINVAL; 15769408Sache 15869408Sache /* Validate ECC block. */ 159100616Smp u8 *ecc = &data[offset]; 160100616Smp 161316958Sdchagin if (ecc[ID] != ECC_BLOCK_ID || ecc[LEN] < BLOCK_SIZE || 162100616Smp ecc[LEN] + offset > size || 163100616Smp ecc[LEN] - BLOCK_SIZE != size_bits / 8 || ecc[VER] != 1 || 164100616Smp ecc[TYP] != 1) 165100616Smp return -EINVAL; 166100616Smp 167100616Smp /* 168100616Smp * Use the header block to locate the ECC block and verify the data 169100616Smp * blocks [2] against the ecc block ECC. 170100616Smp */ 171316958Sdchagin ret = verify_bch(bch_configuration.data_ecc_capability_bits, 172316958Sdchagin bch_configuration.prim_poly, &data[data[LEN]], 173316958Sdchagin offset - data[LEN], &data[offset + BLOCK_SIZE], 174316958Sdchagin ecc[LEN] - BLOCK_SIZE); 175100616Smp if (ret < 0) 176100616Smp return ret; 177100616Smp 178100616Smp /* Stop after ECC. Ignore possible zero padding. */ 179100616Smp size = offset; 180195609Smp 181316958Sdchagin for (;;) { 182316958Sdchagin /* Move to next block. */ 183316958Sdchagin size -= data[LEN]; 184316958Sdchagin data += data[LEN]; 185316958Sdchagin 186100616Smp if (size == 0) { 187100616Smp /* Finished iterating through blocks. */ 188100616Smp return 0; 189100616Smp } 190100616Smp 191316958Sdchagin if (size < BLOCK_SIZE || data[LEN] < BLOCK_SIZE) { 192100616Smp /* Not enough data for a header, or short header. */ 193100616Smp return -EINVAL; 194195609Smp } 195100616Smp 196100616Smp ret = fn(userdata, data[ID], data[VER], data[TYP], 197100616Smp data[LEN] - BLOCK_SIZE, &data[BLOCK_SIZE]); 198100616Smp if (ret) 199100616Smp return ret; 200100616Smp } 201100616Smp} 202100616Smp 203100616Smpint read_i2c_vpd(struct vpd_cache *cache, 204316958Sdchagin int (*process_block)(struct vpd_cache *, u8 id, u8 version, 205100616Smp u8 type, size_t size, u8 const *data)) 206100616Smp{ 207316958Sdchagin struct udevice *dev; 208316958Sdchagin int ret; 209316958Sdchagin u8 *data; 210316958Sdchagin int size; 211316958Sdchagin 212316958Sdchagin ret = uclass_get_device_by_name(UCLASS_I2C_EEPROM, "vpd@0", &dev); 213316958Sdchagin if (ret) 214100616Smp return ret; 215195609Smp 216195609Smp size = i2c_eeprom_size(dev); 217195609Smp if (size < 0) { 218195609Smp printf("Unable to get size of eeprom: %d\n", ret); 219195609Smp return ret; 220195609Smp } 221195609Smp 222195609Smp data = malloc(size); 223195609Smp if (!data) 224195609Smp return -ENOMEM; 225195609Smp 226195609Smp ret = i2c_eeprom_read(dev, 0, data, size); 227100616Smp if (ret) { 228100616Smp free(data); 229100616Smp return ret; 230316958Sdchagin } 231195609Smp 232316958Sdchagin ret = vpd_reader(size, data, cache, process_block); 233316958Sdchagin 234316958Sdchagin free(data); 235316958Sdchagin 236100616Smp return ret; 237195609Smp} 238195609Smp