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