blob: c471583be86bcf2f745d3882b8ad781687bb2ea7 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Martyn Welch66697ce2017-11-08 15:35:15 +00002/*
3 * Copyright 2016 General Electric Company
Martyn Welch66697ce2017-11-08 15:35:15 +00004 */
5
6#include "vpd_reader.h"
7
8#include <linux/bch.h>
9#include <stdlib.h>
10
11/* BCH configuration */
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 * 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
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 * | 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}