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 13 const 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 31 static 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 46 static 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 93 static const int ID; 94 static const int LEN = 1; 95 static const int VER = 2; 96 static const int TYP = 3; 97 static const int BLOCK_SIZE = 4; 98 99 static const u8 HEADER_BLOCK_ID; 100 static const u8 HEADER_BLOCK_LEN = 18; 101 static const u32 HEADER_BLOCK_MAGIC = 0xca53ca53; 102 static const size_t HEADER_BLOCK_VERIFY_LEN = 14; 103 static const size_t HEADER_BLOCK_ECC_OFF = 14; 104 static const size_t HEADER_BLOCK_ECC_LEN = 4; 105 106 static const u8 ECC_BLOCK_ID = 0xFF; 107 108 int 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