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