1
2
3
4
5
6#include "vpd_reader.h"
7
8#include <linux/bch.h>
9#include <stdlib.h>
10
11
12
13const 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
31static 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
46static 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
80
81
82 } else {
83
84 data[errloc[n] / 8] ^= 1 << (errloc[n] % 8);
85 }
86 }
87 }
88
89 free(errloc);
90 return 0;
91}
92
93static const int ID;
94static const int LEN = 1;
95static const int VER = 2;
96static const int TYP = 3;
97static const int BLOCK_SIZE = 4;
98
99static const u8 HEADER_BLOCK_ID;
100static const u8 HEADER_BLOCK_LEN = 18;
101static const u32 HEADER_BLOCK_MAGIC = 0xca53ca53;
102static const size_t HEADER_BLOCK_VERIFY_LEN = 14;
103static const size_t HEADER_BLOCK_ECC_OFF = 14;
104static const size_t HEADER_BLOCK_ECC_LEN = 4;
105
106static const u8 ECC_BLOCK_ID = 0xFF;
107
108int 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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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
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
150 if (offset + 3 >= size)
151 return -EINVAL;
152
153
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
164
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
174 size = offset;
175
176 for (;;) {
177
178 size -= data[LEN];
179 data += data[LEN];
180
181 if (size == 0) {
182
183 return 0;
184 }
185
186 if (size < BLOCK_SIZE || data[LEN] < BLOCK_SIZE) {
187
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