blob: 12410d9b715bc51bb89606351914f0747114774e [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Martyn Welch66697ce2017-11-08 15:35:15 +00002/*
3 * Copyright 2016 General Electric Company
Martyn Welch66697ce2017-11-08 15:35:15 +00004 */
5
6#include "vpd_reader.h"
7
Denis Zalevskiy22a347d2018-10-17 10:33:30 +02008#include <i2c.h>
Martyn Welch66697ce2017-11-08 15:35:15 +00009#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
Denis Zalevskiy22a347d2018-10-17 10:33:30 +0200109static 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))
Martyn Welch66697ce2017-11-08 15:35:15 +0000112{
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}
Denis Zalevskiy22a347d2018-10-17 10:33:30 +0200198
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}