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