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