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