uboot/board/ge/common/vpd_reader.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright 2016 General Electric Company
   4 */
   5
   6#include "vpd_reader.h"
   7#include <malloc.h>
   8
   9#include <i2c.h>
  10#include <linux/bch.h>
  11#include <stdlib.h>
  12#include <dm/uclass.h>
  13#include <i2c_eeprom.h>
  14#include <hexdump.h>
  15
  16/* BCH configuration */
  17
  18const struct {
  19        int header_ecc_capability_bits;
  20        int data_ecc_capability_bits;
  21        unsigned int prim_poly;
  22        struct {
  23                int min;
  24                int max;
  25        } galois_field_order;
  26} bch_configuration = {
  27        .header_ecc_capability_bits = 4,
  28        .data_ecc_capability_bits = 16,
  29        .prim_poly = 0,
  30        .galois_field_order = {
  31                .min = 5,
  32                .max = 15,
  33        },
  34};
  35
  36static int calculate_galois_field_order(size_t source_length)
  37{
  38        int gfo = bch_configuration.galois_field_order.min;
  39
  40        for (; gfo < bch_configuration.galois_field_order.max &&
  41             ((((1 << gfo) - 1) - ((int)source_length * 8)) < 0);
  42             gfo++) {
  43        }
  44
  45        if (gfo == bch_configuration.galois_field_order.max)
  46                return -1;
  47
  48        return gfo + 1;
  49}
  50
  51static int verify_bch(int ecc_bits, unsigned int prim_poly, u8 *data,
  52                      size_t data_length, const u8 *ecc, size_t ecc_length)
  53{
  54        int gfo = calculate_galois_field_order(data_length);
  55
  56        if (gfo < 0)
  57                return -1;
  58
  59        struct bch_control *bch = init_bch(gfo, ecc_bits, prim_poly);
  60
  61        if (!bch)
  62                return -1;
  63
  64        if (bch->ecc_bytes != ecc_length) {
  65                free_bch(bch);
  66                return -1;
  67        }
  68
  69        unsigned int *errloc = (unsigned int *)calloc(data_length,
  70                                                      sizeof(unsigned int));
  71        int errors = decode_bch(bch, data, data_length, ecc, NULL, NULL,
  72                                errloc);
  73
  74        free_bch(bch);
  75        if (errors < 0) {
  76                free(errloc);
  77                return -1;
  78        }
  79
  80        if (errors > 0) {
  81                for (int n = 0; n < errors; n++) {
  82                        if (errloc[n] >= 8 * data_length) {
  83                                /*
  84                                 * n-th error located in ecc (no need for data
  85                                 * correction)
  86                                 */
  87                        } else {
  88                                /* n-th error located in data */
  89                                data[errloc[n] / 8] ^= 1 << (errloc[n] % 8);
  90                        }
  91                }
  92        }
  93
  94        free(errloc);
  95        return 0;
  96}
  97
  98static const int ID;
  99static const int LEN = 1;
 100static const int VER = 2;
 101static const int TYP = 3;
 102static const int BLOCK_SIZE = 4;
 103
 104static const u8 HEADER_BLOCK_ID;
 105static const u8 HEADER_BLOCK_LEN = 18;
 106static const u32 HEADER_BLOCK_MAGIC = 0xca53ca53;
 107static const size_t HEADER_BLOCK_VERIFY_LEN = 14;
 108static const size_t HEADER_BLOCK_ECC_OFF = 14;
 109static const size_t HEADER_BLOCK_ECC_LEN = 4;
 110
 111static const u8 ECC_BLOCK_ID = 0xFF;
 112
 113int vpd_reader(size_t size, u8 *data, struct vpd_cache *userdata,
 114               int (*fn)(struct vpd_cache *, u8 id, u8 version, u8 type,
 115                         size_t size, u8 const *data))
 116{
 117        if (size < HEADER_BLOCK_LEN || !data || !fn)
 118                return -EINVAL;
 119
 120        /*
 121         * +--------------------+----------------+--//--+--------------------+
 122         * | header block       | data block     | ...  | ecc block          |
 123         * +--------------------+----------------+--//--+--------------------+
 124         * :                    :                       :
 125         * +------+-------+-----+                       +------+-------------+
 126         * | id   | magic | ecc |                       | ...  | ecc         |
 127         * | len  | off   |     |                       +------+-------------+
 128         * | ver  | size  |     |                       :
 129         * | type |       |     |                       :
 130         * +------+-------+-----+                       :
 131         * :              :     :                       :
 132         * <----- [1] ---->     <--------- [2] --------->
 133         *
 134         * Repair (if necessary) the contents of header block [1] by using a
 135         * 4 byte ECC located at the end of the header block.  A successful
 136         * return value means that we can trust the header.
 137         */
 138        int ret = verify_bch(bch_configuration.header_ecc_capability_bits,
 139                             bch_configuration.prim_poly, data,
 140                             HEADER_BLOCK_VERIFY_LEN,
 141                             &data[HEADER_BLOCK_ECC_OFF], HEADER_BLOCK_ECC_LEN);
 142        if (ret < 0)
 143                return ret;
 144
 145        /* Validate header block { id, length, version, type }. */
 146        if (data[ID] != HEADER_BLOCK_ID || data[LEN] != HEADER_BLOCK_LEN ||
 147            data[VER] != 0 || data[TYP] != 0 ||
 148            ntohl(*(u32 *)(&data[4])) != HEADER_BLOCK_MAGIC)
 149                return -EINVAL;
 150
 151        u32 offset = ntohl(*(u32 *)(&data[8]));
 152        u16 size_bits = ntohs(*(u16 *)(&data[12]));
 153
 154        /* Check that ECC header fits. */
 155        if (offset + 3 >= size)
 156                return -EINVAL;
 157
 158        /* Validate ECC block. */
 159        u8 *ecc = &data[offset];
 160
 161        if (ecc[ID] != ECC_BLOCK_ID || ecc[LEN] < BLOCK_SIZE ||
 162            ecc[LEN] + offset > size ||
 163            ecc[LEN] - BLOCK_SIZE != size_bits / 8 || ecc[VER] != 1 ||
 164            ecc[TYP] != 1)
 165                return -EINVAL;
 166
 167        /*
 168         * Use the header block to locate the ECC block and verify the data
 169         * blocks [2] against the ecc block ECC.
 170         */
 171        ret = verify_bch(bch_configuration.data_ecc_capability_bits,
 172                         bch_configuration.prim_poly, &data[data[LEN]],
 173                         offset - data[LEN], &data[offset + BLOCK_SIZE],
 174                         ecc[LEN] - BLOCK_SIZE);
 175        if (ret < 0)
 176                return ret;
 177
 178        /* Stop after ECC.  Ignore possible zero padding. */
 179        size = offset;
 180
 181        for (;;) {
 182                /* Move to next block. */
 183                size -= data[LEN];
 184                data += data[LEN];
 185
 186                if (size == 0) {
 187                        /* Finished iterating through blocks. */
 188                        return 0;
 189                }
 190
 191                if (size < BLOCK_SIZE || data[LEN] < BLOCK_SIZE) {
 192                        /* Not enough data for a header, or short header. */
 193                        return -EINVAL;
 194                }
 195
 196                ret = fn(userdata, data[ID], data[VER], data[TYP],
 197                         data[LEN] - BLOCK_SIZE, &data[BLOCK_SIZE]);
 198                if (ret)
 199                        return ret;
 200        }
 201}
 202
 203int read_i2c_vpd(struct vpd_cache *cache,
 204                 int (*process_block)(struct vpd_cache *, u8 id, u8 version,
 205                                      u8 type, size_t size, u8 const *data))
 206{
 207        struct udevice *dev;
 208        int ret;
 209        u8 *data;
 210        int size;
 211
 212        ret = uclass_get_device_by_name(UCLASS_I2C_EEPROM, "vpd@0", &dev);
 213        if (ret)
 214                return ret;
 215
 216        size = i2c_eeprom_size(dev);
 217        if (size < 0) {
 218                printf("Unable to get size of eeprom: %d\n", ret);
 219                return ret;
 220        }
 221
 222        data = malloc(size);
 223        if (!data)
 224                return -ENOMEM;
 225
 226        ret = i2c_eeprom_read(dev, 0, data, size);
 227        if (ret) {
 228                free(data);
 229                return ret;
 230        }
 231
 232        ret = vpd_reader(size, data, cache, process_block);
 233
 234        free(data);
 235
 236        return ret;
 237}
 238