1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47#include <asm/byteorder.h>
48#include <asm/unaligned.h>
49#include <linux/delay.h>
50#include <linux/log2.h>
51#include <linux/kernel.h>
52#include <linux/module.h>
53#include <linux/slab.h>
54
55#define NFP_SUBSYS "nfp_hwinfo"
56
57#include "crc32.h"
58#include "nfp.h"
59#include "nfp_cpp.h"
60#include "nfp6000/nfp6000.h"
61
62#define HWINFO_SIZE_MIN 0x100
63#define HWINFO_WAIT 20
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113#define NFP_HWINFO_VERSION_1 ('H' << 24 | 'I' << 16 | 1 << 8 | 0 << 1 | 0)
114#define NFP_HWINFO_VERSION_2 ('H' << 24 | 'I' << 16 | 2 << 8 | 0 << 1 | 0)
115#define NFP_HWINFO_VERSION_UPDATING BIT(0)
116
117struct nfp_hwinfo {
118 u8 start[0];
119
120 __le32 version;
121 __le32 size;
122
123
124 __le32 limit;
125 __le32 resv;
126
127 char data[];
128};
129
130static bool nfp_hwinfo_is_updating(struct nfp_hwinfo *hwinfo)
131{
132 return le32_to_cpu(hwinfo->version) & NFP_HWINFO_VERSION_UPDATING;
133}
134
135static int
136hwinfo_db_walk(struct nfp_cpp *cpp, struct nfp_hwinfo *hwinfo, u32 size)
137{
138 const char *key, *val, *end = hwinfo->data + size;
139
140 for (key = hwinfo->data; *key && key < end;
141 key = val + strlen(val) + 1) {
142
143 val = key + strlen(key) + 1;
144 if (val >= end) {
145 nfp_warn(cpp, "Bad HWINFO - overflowing key\n");
146 return -EINVAL;
147 }
148
149 if (val + strlen(val) + 1 > end) {
150 nfp_warn(cpp, "Bad HWINFO - overflowing value\n");
151 return -EINVAL;
152 }
153 }
154
155 return 0;
156}
157
158static int
159hwinfo_db_validate(struct nfp_cpp *cpp, struct nfp_hwinfo *db, u32 len)
160{
161 u32 size, crc;
162
163 size = le32_to_cpu(db->size);
164 if (size > len) {
165 nfp_err(cpp, "Unsupported hwinfo size %u > %u\n", size, len);
166 return -EINVAL;
167 }
168
169 size -= sizeof(u32);
170 crc = crc32_posix(db, size);
171 if (crc != get_unaligned_le32(db->start + size)) {
172 nfp_err(cpp, "Corrupt hwinfo table (CRC mismatch), calculated 0x%x, expected 0x%x\n",
173 crc, get_unaligned_le32(db->start + size));
174
175 return -EINVAL;
176 }
177
178 return hwinfo_db_walk(cpp, db, size);
179}
180
181static struct nfp_hwinfo *
182hwinfo_try_fetch(struct nfp_cpp *cpp, size_t *cpp_size)
183{
184 struct nfp_hwinfo *header;
185 struct nfp_resource *res;
186 u64 cpp_addr;
187 u32 cpp_id;
188 int err;
189 u8 *db;
190
191 res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_HWINFO);
192 if (!IS_ERR(res)) {
193 cpp_id = nfp_resource_cpp_id(res);
194 cpp_addr = nfp_resource_address(res);
195 *cpp_size = nfp_resource_size(res);
196
197 nfp_resource_release(res);
198
199 if (*cpp_size < HWINFO_SIZE_MIN)
200 return NULL;
201 } else if (PTR_ERR(res) == -ENOENT) {
202
203 cpp_id = NFP_CPP_ISLAND_ID(NFP_CPP_TARGET_MU,
204 NFP_CPP_ACTION_RW, 0, 1);
205 cpp_addr = 0x30000;
206 *cpp_size = 0x0e000;
207 } else {
208 return NULL;
209 }
210
211 db = kmalloc(*cpp_size + 1, GFP_KERNEL);
212 if (!db)
213 return NULL;
214
215 err = nfp_cpp_read(cpp, cpp_id, cpp_addr, db, *cpp_size);
216 if (err != *cpp_size)
217 goto exit_free;
218
219 header = (void *)db;
220 if (nfp_hwinfo_is_updating(header))
221 goto exit_free;
222
223 if (le32_to_cpu(header->version) != NFP_HWINFO_VERSION_2) {
224 nfp_err(cpp, "Unknown HWInfo version: 0x%08x\n",
225 le32_to_cpu(header->version));
226 goto exit_free;
227 }
228
229
230 db[*cpp_size] = '\0';
231
232 return (void *)db;
233exit_free:
234 kfree(db);
235 return NULL;
236}
237
238static struct nfp_hwinfo *hwinfo_fetch(struct nfp_cpp *cpp, size_t *hwdb_size)
239{
240 const unsigned long wait_until = jiffies + HWINFO_WAIT * HZ;
241 struct nfp_hwinfo *db;
242 int err;
243
244 for (;;) {
245 const unsigned long start_time = jiffies;
246
247 db = hwinfo_try_fetch(cpp, hwdb_size);
248 if (db)
249 return db;
250
251 err = msleep_interruptible(100);
252 if (err || time_after(start_time, wait_until)) {
253 nfp_err(cpp, "NFP access error\n");
254 return NULL;
255 }
256 }
257}
258
259struct nfp_hwinfo *nfp_hwinfo_read(struct nfp_cpp *cpp)
260{
261 struct nfp_hwinfo *db;
262 size_t hwdb_size = 0;
263 int err;
264
265 db = hwinfo_fetch(cpp, &hwdb_size);
266 if (!db)
267 return NULL;
268
269 err = hwinfo_db_validate(cpp, db, hwdb_size);
270 if (err) {
271 kfree(db);
272 return NULL;
273 }
274
275 return db;
276}
277
278
279
280
281
282
283
284
285const char *nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup)
286{
287 const char *key, *val, *end;
288
289 if (!hwinfo || !lookup)
290 return NULL;
291
292 end = hwinfo->data + le32_to_cpu(hwinfo->size) - sizeof(u32);
293
294 for (key = hwinfo->data; *key && key < end;
295 key = val + strlen(val) + 1) {
296
297 val = key + strlen(key) + 1;
298
299 if (strcmp(key, lookup) == 0)
300 return val;
301 }
302
303 return NULL;
304}
305
306char *nfp_hwinfo_get_packed_strings(struct nfp_hwinfo *hwinfo)
307{
308 return hwinfo->data;
309}
310
311u32 nfp_hwinfo_get_packed_str_size(struct nfp_hwinfo *hwinfo)
312{
313 return le32_to_cpu(hwinfo->size) - sizeof(u32);
314}
315