0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include <asm/byteorder.h>
0018 #include <asm/unaligned.h>
0019 #include <linux/delay.h>
0020 #include <linux/log2.h>
0021 #include <linux/kernel.h>
0022 #include <linux/module.h>
0023 #include <linux/slab.h>
0024
0025 #define NFP_SUBSYS "nfp_hwinfo"
0026
0027 #include "crc32.h"
0028 #include "nfp.h"
0029 #include "nfp_cpp.h"
0030 #include "nfp6000/nfp6000.h"
0031
0032 #define HWINFO_SIZE_MIN 0x100
0033 #define HWINFO_WAIT 20
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075
0076
0077
0078
0079
0080
0081
0082
0083 #define NFP_HWINFO_VERSION_1 ('H' << 24 | 'I' << 16 | 1 << 8 | 0 << 1 | 0)
0084 #define NFP_HWINFO_VERSION_2 ('H' << 24 | 'I' << 16 | 2 << 8 | 0 << 1 | 0)
0085 #define NFP_HWINFO_VERSION_UPDATING BIT(0)
0086
0087 struct nfp_hwinfo {
0088 u8 start[0];
0089
0090 __le32 version;
0091 __le32 size;
0092
0093
0094 __le32 limit;
0095 __le32 resv;
0096
0097 char data[];
0098 };
0099
0100 static bool nfp_hwinfo_is_updating(struct nfp_hwinfo *hwinfo)
0101 {
0102 return le32_to_cpu(hwinfo->version) & NFP_HWINFO_VERSION_UPDATING;
0103 }
0104
0105 static int
0106 hwinfo_db_walk(struct nfp_cpp *cpp, struct nfp_hwinfo *hwinfo, u32 size)
0107 {
0108 const char *key, *val, *end = hwinfo->data + size;
0109
0110 for (key = hwinfo->data; *key && key < end;
0111 key = val + strlen(val) + 1) {
0112
0113 val = key + strlen(key) + 1;
0114 if (val >= end) {
0115 nfp_warn(cpp, "Bad HWINFO - overflowing key\n");
0116 return -EINVAL;
0117 }
0118
0119 if (val + strlen(val) + 1 > end) {
0120 nfp_warn(cpp, "Bad HWINFO - overflowing value\n");
0121 return -EINVAL;
0122 }
0123 }
0124
0125 return 0;
0126 }
0127
0128 static int
0129 hwinfo_db_validate(struct nfp_cpp *cpp, struct nfp_hwinfo *db, u32 len)
0130 {
0131 u32 size, crc;
0132
0133 size = le32_to_cpu(db->size);
0134 if (size > len) {
0135 nfp_err(cpp, "Unsupported hwinfo size %u > %u\n", size, len);
0136 return -EINVAL;
0137 }
0138
0139 size -= sizeof(u32);
0140 crc = crc32_posix(db, size);
0141 if (crc != get_unaligned_le32(db->start + size)) {
0142 nfp_err(cpp, "Corrupt hwinfo table (CRC mismatch), calculated 0x%x, expected 0x%x\n",
0143 crc, get_unaligned_le32(db->start + size));
0144
0145 return -EINVAL;
0146 }
0147
0148 return hwinfo_db_walk(cpp, db, size);
0149 }
0150
0151 static struct nfp_hwinfo *
0152 hwinfo_try_fetch(struct nfp_cpp *cpp, size_t *cpp_size)
0153 {
0154 struct nfp_hwinfo *header;
0155 struct nfp_resource *res;
0156 u64 cpp_addr;
0157 u32 cpp_id;
0158 int err;
0159 u8 *db;
0160
0161 res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_HWINFO);
0162 if (!IS_ERR(res)) {
0163 cpp_id = nfp_resource_cpp_id(res);
0164 cpp_addr = nfp_resource_address(res);
0165 *cpp_size = nfp_resource_size(res);
0166
0167 nfp_resource_release(res);
0168
0169 if (*cpp_size < HWINFO_SIZE_MIN)
0170 return NULL;
0171 } else if (PTR_ERR(res) == -ENOENT) {
0172
0173 cpp_id = NFP_CPP_ISLAND_ID(NFP_CPP_TARGET_MU,
0174 NFP_CPP_ACTION_RW, 0, 1);
0175 cpp_addr = 0x30000;
0176 *cpp_size = 0x0e000;
0177 } else {
0178 return NULL;
0179 }
0180
0181 db = kmalloc(*cpp_size + 1, GFP_KERNEL);
0182 if (!db)
0183 return NULL;
0184
0185 err = nfp_cpp_read(cpp, cpp_id, cpp_addr, db, *cpp_size);
0186 if (err != *cpp_size)
0187 goto exit_free;
0188
0189 header = (void *)db;
0190 if (nfp_hwinfo_is_updating(header))
0191 goto exit_free;
0192
0193 if (le32_to_cpu(header->version) != NFP_HWINFO_VERSION_2) {
0194 nfp_err(cpp, "Unknown HWInfo version: 0x%08x\n",
0195 le32_to_cpu(header->version));
0196 goto exit_free;
0197 }
0198
0199
0200 db[*cpp_size] = '\0';
0201
0202 return (void *)db;
0203 exit_free:
0204 kfree(db);
0205 return NULL;
0206 }
0207
0208 static struct nfp_hwinfo *hwinfo_fetch(struct nfp_cpp *cpp, size_t *hwdb_size)
0209 {
0210 const unsigned long wait_until = jiffies + HWINFO_WAIT * HZ;
0211 struct nfp_hwinfo *db;
0212 int err;
0213
0214 for (;;) {
0215 const unsigned long start_time = jiffies;
0216
0217 db = hwinfo_try_fetch(cpp, hwdb_size);
0218 if (db)
0219 return db;
0220
0221 err = msleep_interruptible(100);
0222 if (err || time_after(start_time, wait_until)) {
0223 nfp_err(cpp, "NFP access error\n");
0224 return NULL;
0225 }
0226 }
0227 }
0228
0229 struct nfp_hwinfo *nfp_hwinfo_read(struct nfp_cpp *cpp)
0230 {
0231 struct nfp_hwinfo *db;
0232 size_t hwdb_size = 0;
0233 int err;
0234
0235 db = hwinfo_fetch(cpp, &hwdb_size);
0236 if (!db)
0237 return NULL;
0238
0239 err = hwinfo_db_validate(cpp, db, hwdb_size);
0240 if (err) {
0241 kfree(db);
0242 return NULL;
0243 }
0244
0245 return db;
0246 }
0247
0248
0249
0250
0251
0252
0253
0254
0255 const char *nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup)
0256 {
0257 const char *key, *val, *end;
0258
0259 if (!hwinfo || !lookup)
0260 return NULL;
0261
0262 end = hwinfo->data + le32_to_cpu(hwinfo->size) - sizeof(u32);
0263
0264 for (key = hwinfo->data; *key && key < end;
0265 key = val + strlen(val) + 1) {
0266
0267 val = key + strlen(key) + 1;
0268
0269 if (strcmp(key, lookup) == 0)
0270 return val;
0271 }
0272
0273 return NULL;
0274 }
0275
0276 char *nfp_hwinfo_get_packed_strings(struct nfp_hwinfo *hwinfo)
0277 {
0278 return hwinfo->data;
0279 }
0280
0281 u32 nfp_hwinfo_get_packed_str_size(struct nfp_hwinfo *hwinfo)
0282 {
0283 return le32_to_cpu(hwinfo->size) - sizeof(u32);
0284 }