1 /* 2 * Copyright 2016 General Electric Company 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include "vpd_reader.h" 8 9 #include <linux/bch.h> 10 #include <stdlib.h> 11 12 /* BCH configuration */ 13 14 const 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 32 static 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 47 static 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 94 static const int ID; 95 static const int LEN = 1; 96 static const int VER = 2; 97 static const int TYP = 3; 98 static const int BLOCK_SIZE = 4; 99 100 static const u8 HEADER_BLOCK_ID; 101 static const u8 HEADER_BLOCK_LEN = 18; 102 static const u32 HEADER_BLOCK_MAGIC = 0xca53ca53; 103 static const size_t HEADER_BLOCK_VERIFY_LEN = 14; 104 static const size_t HEADER_BLOCK_ECC_OFF = 14; 105 static const size_t HEADER_BLOCK_ECC_LEN = 4; 106 107 static const u8 ECC_BLOCK_ID = 0xFF; 108 109 int vpd_reader(size_t size, u8 *data, void *userdata, 110 int (*fn)(void *userdata, 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