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