]> git.sur5r.net Git - u-boot/blob - board/ge/common/vpd_reader.c
7367427993b32162b6cefee6f46c84835ab25c4b
[u-boot] / board / ge / common / vpd_reader.c
1 /*
2  * Copyright 2016 General Electric Company
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6
7 #include "vpd_reader.h"
8
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
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
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
109 int vpd_reader(size_t size, u8 *data, void *userdata,
110                int (*fn)(void *userdata, 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 }