1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018 Netronome Systems, Inc. 3 * All rights reserved. 4 */ 5 6 /* 7 * Parse the hwinfo table that the ARM firmware builds in the ARM scratch SRAM 8 * after chip reset. 9 * 10 * Examples of the fields: 11 * me.count = 40 12 * me.mask = 0x7f_ffff_ffff 13 * 14 * me.count is the total number of MEs on the system. 15 * me.mask is the bitmask of MEs that are available for application usage. 16 * 17 * (ie, in this example, ME 39 has been reserved by boardconfig.) 18 */ 19 20 #include "nfp_hwinfo.h" 21 22 #include "nfp_crc.h" 23 #include "nfp_logs.h" 24 #include "nfp_resource.h" 25 26 #define HWINFO_SIZE_MIN 0x100 27 28 /* 29 * The Hardware Info Table defines the properties of the system. 30 * 31 * HWInfo v1 Table (fixed size) 32 * 33 * 0x0000: uint32_t version Hardware Info Table version (1.0) 34 * 0x0004: uint32_t size Total size of the table, including the 35 * CRC32 (IEEE 802.3) 36 * 0x0008: uint32_t jumptab Offset of key/value table 37 * 0x000c: uint32_t keys Total number of keys in the key/value table 38 * NNNNNN: Key/value jump table and string data 39 * (size - 4): uint32_t crc32 CRC32 (same as IEEE 802.3, POSIX csum, etc) 40 * CRC32("",0) = ~0, CRC32("a",1) = 0x48C279FE 41 * 42 * HWInfo v2 Table (variable size) 43 * 44 * 0x0000: uint32_t version Hardware Info Table version (2.0) 45 * 0x0004: uint32_t size Current size of the data area, excluding CRC32 46 * 0x0008: uint32_t limit Maximum size of the table 47 * 0x000c: uint32_t reserved Unused, set to zero 48 * NNNNNN: Key/value data 49 * (size - 4): uint32_t crc32 CRC32 (same as IEEE 802.3, POSIX csum, etc) 50 * CRC32("",0) = ~0, CRC32("a",1) = 0x48C279FE 51 * 52 * If the HWInfo table is in the process of being updated, the low bit of 53 * version will be set. 54 * 55 * HWInfo v1 Key/Value Table 56 * ------------------------- 57 * 58 * The key/value table is a set of offsets to ASCIIZ strings which have 59 * been strcmp(3) sorted (yes, please use bsearch(3) on the table). 60 * 61 * All keys are guaranteed to be unique. 62 * 63 * N+0: uint32_t key_1 Offset to the first key 64 * N+4: uint32_t val_1 Offset to the first value 65 * N+8: uint32_t key_2 Offset to the second key 66 * N+c: uint32_t val_2 Offset to the second value 67 * ... 68 * 69 * HWInfo v2 Key/Value Table 70 * ------------------------- 71 * 72 * Packed UTF8Z strings, ie 'key1\000value1\000key2\000value2\000' 73 * Unsorted. 74 * 75 * Note: Only the HwInfo v2 Table be supported now. 76 */ 77 78 #define NFP_HWINFO_VERSION_1 ('H' << 24 | 'I' << 16 | 1 << 8 | 0 << 1 | 0) 79 #define NFP_HWINFO_VERSION_2 ('H' << 24 | 'I' << 16 | 2 << 8 | 0 << 1 | 0) 80 #define NFP_HWINFO_VERSION_UPDATING RTE_BIT32(0) 81 82 struct nfp_hwinfo { 83 uint8_t start[0]; 84 85 uint32_t version; 86 uint32_t size; 87 88 /* V2 specific fields */ 89 uint32_t limit; 90 uint32_t resv; 91 92 char data[]; 93 }; 94 95 static bool 96 nfp_hwinfo_is_updating(struct nfp_hwinfo *hwinfo) 97 { 98 return hwinfo->version & NFP_HWINFO_VERSION_UPDATING; 99 } 100 101 static int 102 nfp_hwinfo_db_walk(struct nfp_hwinfo *hwinfo, 103 uint32_t size) 104 { 105 const char *key; 106 const char *val; 107 const char *end = hwinfo->data + size; 108 109 for (key = hwinfo->data; *key != 0 && key < end; 110 key = val + strlen(val) + 1) { 111 val = key + strlen(key) + 1; 112 if (val >= end) { 113 PMD_DRV_LOG(ERR, "Bad HWINFO - overflowing value."); 114 return -EINVAL; 115 } 116 117 if (val + strlen(val) + 1 > end) { 118 PMD_DRV_LOG(ERR, "Bad HWINFO - overflowing value."); 119 return -EINVAL; 120 } 121 } 122 123 return 0; 124 } 125 126 static int 127 nfp_hwinfo_db_validate(struct nfp_hwinfo *db, 128 uint32_t len) 129 { 130 uint32_t *crc; 131 uint32_t size; 132 uint32_t new_crc; 133 134 size = db->size; 135 if (size > len) { 136 PMD_DRV_LOG(ERR, "Unsupported hwinfo size %u > %u.", size, len); 137 return -EINVAL; 138 } 139 140 size -= sizeof(uint32_t); 141 new_crc = nfp_crc32_posix((char *)db, size); 142 crc = (uint32_t *)(db->start + size); 143 if (new_crc != *crc) { 144 PMD_DRV_LOG(ERR, "CRC mismatch, calculated %#x, expected %#x.", 145 new_crc, *crc); 146 return -EINVAL; 147 } 148 149 return nfp_hwinfo_db_walk(db, size); 150 } 151 152 static struct nfp_hwinfo * 153 nfp_hwinfo_try_fetch(struct nfp_cpp *cpp, 154 size_t *cpp_size) 155 { 156 int err; 157 void *res; 158 uint8_t *db; 159 uint32_t cpp_id; 160 uint64_t cpp_addr; 161 struct nfp_hwinfo *header; 162 163 res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_HWINFO); 164 if (res == NULL) { 165 PMD_DRV_LOG(ERR, "HWInfo - acquire resource failed."); 166 return NULL; 167 } 168 169 cpp_id = nfp_resource_cpp_id(res); 170 cpp_addr = nfp_resource_address(res); 171 *cpp_size = nfp_resource_size(res); 172 173 nfp_resource_release(res); 174 175 if (*cpp_size < HWINFO_SIZE_MIN) 176 return NULL; 177 178 db = malloc(*cpp_size + 1); 179 if (db == NULL) 180 return NULL; 181 182 err = nfp_cpp_read(cpp, cpp_id, cpp_addr, db, *cpp_size); 183 if (err != (int)*cpp_size) { 184 PMD_DRV_LOG(ERR, "HWInfo - CPP read error %d.", err); 185 goto exit_free; 186 } 187 188 header = (struct nfp_hwinfo *)db; 189 if (nfp_hwinfo_is_updating(header)) 190 goto exit_free; 191 192 if (header->version != NFP_HWINFO_VERSION_2) { 193 PMD_DRV_LOG(ERR, "Unknown HWInfo version: %#08x.", 194 header->version); 195 goto exit_free; 196 } 197 198 /* NULL-terminate for safety */ 199 db[*cpp_size] = '\0'; 200 201 return (struct nfp_hwinfo *)db; 202 203 exit_free: 204 free(db); 205 return NULL; 206 } 207 208 static struct nfp_hwinfo * 209 nfp_hwinfo_fetch(struct nfp_cpp *cpp, 210 size_t *hwdb_size) 211 { 212 int count = 0; 213 struct timespec wait; 214 struct nfp_hwinfo *db; 215 216 wait.tv_sec = 0; 217 wait.tv_nsec = 10000000; /* 10ms */ 218 219 for (;;) { 220 db = nfp_hwinfo_try_fetch(cpp, hwdb_size); 221 if (db != NULL) 222 return db; 223 224 nanosleep(&wait, NULL); 225 if (count++ > 200) { /* 10ms * 200 = 2s */ 226 PMD_DRV_LOG(ERR, "NFP access error."); 227 return NULL; 228 } 229 } 230 } 231 232 struct nfp_hwinfo * 233 nfp_hwinfo_read(struct nfp_cpp *cpp) 234 { 235 int err; 236 size_t hwdb_size = 0; 237 struct nfp_hwinfo *db; 238 239 db = nfp_hwinfo_fetch(cpp, &hwdb_size); 240 if (db == NULL) 241 return NULL; 242 243 err = nfp_hwinfo_db_validate(db, hwdb_size); 244 if (err != 0) { 245 free(db); 246 return NULL; 247 } 248 249 return db; 250 } 251 252 /** 253 * Find a value in the HWInfo table by name 254 * 255 * @param hwinfo 256 * NFP HWInfo table 257 * @param lookup 258 * HWInfo name to search for 259 * 260 * @return 261 * Value of the HWInfo name, or NULL 262 */ 263 const char * 264 nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, 265 const char *lookup) 266 { 267 const char *key; 268 const char *val; 269 const char *end; 270 271 if (hwinfo == NULL || lookup == NULL) 272 return NULL; 273 274 end = hwinfo->data + hwinfo->size - sizeof(uint32_t); 275 276 for (key = hwinfo->data; *key != 0 && key < end; 277 key = val + strlen(val) + 1) { 278 val = key + strlen(key) + 1; 279 280 if (strcmp(key, lookup) == 0) 281 return val; 282 } 283 284 return NULL; 285 } 286