]> git.sur5r.net Git - u-boot/blob - board/ge/common/vpd_reader.c
SPDX: Convert all of our single license tags to Linux Kernel style
[u-boot] / board / ge / common / vpd_reader.c
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 }