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