Loading...
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 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 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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) /* Copyright (C) 2015-2017 Netronome Systems, Inc. */ /* Parse the hwinfo table that the ARM firmware builds in the ARM scratch SRAM * after chip reset. * * Examples of the fields: * me.count = 40 * me.mask = 0x7f_ffff_ffff * * me.count is the total number of MEs on the system. * me.mask is the bitmask of MEs that are available for application usage. * * (ie, in this example, ME 39 has been reserved by boardconfig.) */ #include <asm/byteorder.h> #include <asm/unaligned.h> #include <linux/delay.h> #include <linux/log2.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> #define NFP_SUBSYS "nfp_hwinfo" #include "crc32.h" #include "nfp.h" #include "nfp_cpp.h" #include "nfp6000/nfp6000.h" #define HWINFO_SIZE_MIN 0x100 #define HWINFO_WAIT 20 /* seconds */ /* The Hardware Info Table defines the properties of the system. * * HWInfo v1 Table (fixed size) * * 0x0000: u32 version Hardware Info Table version (1.0) * 0x0004: u32 size Total size of the table, including * the CRC32 (IEEE 802.3) * 0x0008: u32 jumptab Offset of key/value table * 0x000c: u32 keys Total number of keys in the key/value table * NNNNNN: Key/value jump table and string data * (size - 4): u32 crc32 CRC32 (same as IEEE 802.3, POSIX csum, etc) * CRC32("",0) = ~0, CRC32("a",1) = 0x48C279FE * * HWInfo v2 Table (variable size) * * 0x0000: u32 version Hardware Info Table version (2.0) * 0x0004: u32 size Current size of the data area, excluding CRC32 * 0x0008: u32 limit Maximum size of the table * 0x000c: u32 reserved Unused, set to zero * NNNNNN: Key/value data * (size - 4): u32 crc32 CRC32 (same as IEEE 802.3, POSIX csum, etc) * CRC32("",0) = ~0, CRC32("a",1) = 0x48C279FE * * If the HWInfo table is in the process of being updated, the low bit * of version will be set. * * HWInfo v1 Key/Value Table * ------------------------- * * The key/value table is a set of offsets to ASCIIZ strings which have * been strcmp(3) sorted (yes, please use bsearch(3) on the table). * * All keys are guaranteed to be unique. * * N+0: u32 key_1 Offset to the first key * N+4: u32 val_1 Offset to the first value * N+8: u32 key_2 Offset to the second key * N+c: u32 val_2 Offset to the second value * ... * * HWInfo v2 Key/Value Table * ------------------------- * * Packed UTF8Z strings, ie 'key1\000value1\000key2\000value2\000' * * Unsorted. */ #define NFP_HWINFO_VERSION_1 ('H' << 24 | 'I' << 16 | 1 << 8 | 0 << 1 | 0) #define NFP_HWINFO_VERSION_2 ('H' << 24 | 'I' << 16 | 2 << 8 | 0 << 1 | 0) #define NFP_HWINFO_VERSION_UPDATING BIT(0) struct nfp_hwinfo { u8 start[0]; __le32 version; __le32 size; /* v2 specific fields */ __le32 limit; __le32 resv; char data[]; }; static bool nfp_hwinfo_is_updating(struct nfp_hwinfo *hwinfo) { return le32_to_cpu(hwinfo->version) & NFP_HWINFO_VERSION_UPDATING; } static int hwinfo_db_walk(struct nfp_cpp *cpp, struct nfp_hwinfo *hwinfo, u32 size) { const char *key, *val, *end = hwinfo->data + size; for (key = hwinfo->data; *key && key < end; key = val + strlen(val) + 1) { val = key + strlen(key) + 1; if (val >= end) { nfp_warn(cpp, "Bad HWINFO - overflowing key\n"); return -EINVAL; } if (val + strlen(val) + 1 > end) { nfp_warn(cpp, "Bad HWINFO - overflowing value\n"); return -EINVAL; } } return 0; } static int hwinfo_db_validate(struct nfp_cpp *cpp, struct nfp_hwinfo *db, u32 len) { u32 size, crc; size = le32_to_cpu(db->size); if (size > len) { nfp_err(cpp, "Unsupported hwinfo size %u > %u\n", size, len); return -EINVAL; } size -= sizeof(u32); crc = crc32_posix(db, size); if (crc != get_unaligned_le32(db->start + size)) { nfp_err(cpp, "Corrupt hwinfo table (CRC mismatch), calculated 0x%x, expected 0x%x\n", crc, get_unaligned_le32(db->start + size)); return -EINVAL; } return hwinfo_db_walk(cpp, db, size); } static struct nfp_hwinfo * hwinfo_try_fetch(struct nfp_cpp *cpp, size_t *cpp_size) { struct nfp_hwinfo *header; struct nfp_resource *res; u64 cpp_addr; u32 cpp_id; int err; u8 *db; res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_HWINFO); if (!IS_ERR(res)) { cpp_id = nfp_resource_cpp_id(res); cpp_addr = nfp_resource_address(res); *cpp_size = nfp_resource_size(res); nfp_resource_release(res); if (*cpp_size < HWINFO_SIZE_MIN) return NULL; } else if (PTR_ERR(res) == -ENOENT) { /* Try getting the HWInfo table from the 'classic' location */ cpp_id = NFP_CPP_ISLAND_ID(NFP_CPP_TARGET_MU, NFP_CPP_ACTION_RW, 0, 1); cpp_addr = 0x30000; *cpp_size = 0x0e000; } else { return NULL; } db = kmalloc(*cpp_size + 1, GFP_KERNEL); if (!db) return NULL; err = nfp_cpp_read(cpp, cpp_id, cpp_addr, db, *cpp_size); if (err != *cpp_size) goto exit_free; header = (void *)db; if (nfp_hwinfo_is_updating(header)) goto exit_free; if (le32_to_cpu(header->version) != NFP_HWINFO_VERSION_2) { nfp_err(cpp, "Unknown HWInfo version: 0x%08x\n", le32_to_cpu(header->version)); goto exit_free; } /* NULL-terminate for safety */ db[*cpp_size] = '\0'; return (void *)db; exit_free: kfree(db); return NULL; } static struct nfp_hwinfo *hwinfo_fetch(struct nfp_cpp *cpp, size_t *hwdb_size) { const unsigned long wait_until = jiffies + HWINFO_WAIT * HZ; struct nfp_hwinfo *db; int err; for (;;) { const unsigned long start_time = jiffies; db = hwinfo_try_fetch(cpp, hwdb_size); if (db) return db; err = msleep_interruptible(100); if (err || time_after(start_time, wait_until)) { nfp_err(cpp, "NFP access error\n"); return NULL; } } } struct nfp_hwinfo *nfp_hwinfo_read(struct nfp_cpp *cpp) { struct nfp_hwinfo *db; size_t hwdb_size = 0; int err; db = hwinfo_fetch(cpp, &hwdb_size); if (!db) return NULL; err = hwinfo_db_validate(cpp, db, hwdb_size); if (err) { kfree(db); return NULL; } return db; } /** * nfp_hwinfo_lookup() - Find a value in the HWInfo table by name * @hwinfo: NFP HWinfo table * @lookup: HWInfo name to search for * * Return: Value of the HWInfo name, or NULL */ const char *nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup) { const char *key, *val, *end; if (!hwinfo || !lookup) return NULL; end = hwinfo->data + le32_to_cpu(hwinfo->size) - sizeof(u32); for (key = hwinfo->data; *key && key < end; key = val + strlen(val) + 1) { val = key + strlen(key) + 1; if (strcmp(key, lookup) == 0) return val; } return NULL; } char *nfp_hwinfo_get_packed_strings(struct nfp_hwinfo *hwinfo) { return hwinfo->data; } u32 nfp_hwinfo_get_packed_str_size(struct nfp_hwinfo *hwinfo) { return le32_to_cpu(hwinfo->size) - sizeof(u32); } |