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 
18 const 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 
calculate_galois_field_order(size_t source_length)36 static 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 
verify_bch(int ecc_bits,unsigned int prim_poly,u8 * data,size_t data_length,const u8 * ecc,size_t ecc_length)51 static 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 
98 static const int ID;
99 static const int LEN = 1;
100 static const int VER = 2;
101 static const int TYP = 3;
102 static const int BLOCK_SIZE = 4;
103 
104 static const u8 HEADER_BLOCK_ID;
105 static const u8 HEADER_BLOCK_LEN = 18;
106 static const u32 HEADER_BLOCK_MAGIC = 0xca53ca53;
107 static const size_t HEADER_BLOCK_VERIFY_LEN = 14;
108 static const size_t HEADER_BLOCK_ECC_OFF = 14;
109 static const size_t HEADER_BLOCK_ECC_LEN = 4;
110 
111 static const u8 ECC_BLOCK_ID = 0xFF;
112 
vpd_reader(size_t size,u8 * data,struct vpd_cache * userdata,int (* fn)(struct vpd_cache *,u8 id,u8 version,u8 type,size_t size,u8 const * data))113 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))
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 
read_i2c_vpd(struct vpd_cache * cache,int (* process_block)(struct vpd_cache *,u8 id,u8 version,u8 type,size_t size,u8 const * data))203 int 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