1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2016 General Electric Company
4 */
5
6#include "vpd_reader.h"
7#include <malloc.h>
8
9#include <i2c.h>
10#include <linux/bch.h>
11#include <stdlib.h>
12#include <dm/uclass.h>
13#include <i2c_eeprom.h>
14#include <hexdump.h>
15
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
113int 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))
116{
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}
202
203int read_i2c_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{
207	struct udevice *dev;
208	int ret;
209	u8 *data;
210	int size;
211
212	ret = uclass_get_device_by_name(UCLASS_I2C_EEPROM, "vpd@0", &dev);
213	if (ret)
214		return ret;
215
216	size = i2c_eeprom_size(dev);
217	if (size < 0) {
218		printf("Unable to get size of eeprom: %d\n", ret);
219		return ret;
220	}
221
222	data = malloc(size);
223	if (!data)
224		return -ENOMEM;
225
226	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);
233
234	free(data);
235
236	return ret;
237}
238