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