1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2018 Netronome Systems, Inc. 3 * All rights reserved. 4 */ 5 6 /* Parse the hwinfo table that the ARM firmware builds in the ARM scratch SRAM 7 * after chip reset. 8 * 9 * Examples of the fields: 10 * me.count = 40 11 * me.mask = 0x7f_ffff_ffff 12 * 13 * me.count is the total number of MEs on the system. 14 * me.mask is the bitmask of MEs that are available for application usage. 15 * 16 * (ie, in this example, ME 39 has been reserved by boardconfig.) 17 */ 18 19 #include <stdio.h> 20 #include <time.h> 21 22 #include "nfp_cpp.h" 23 #include "nfp_logs.h" 24 #include "nfp6000/nfp6000.h" 25 #include "nfp_resource.h" 26 #include "nfp_hwinfo.h" 27 #include "nfp_crc.h" 28 29 static int 30 nfp_hwinfo_is_updating(struct nfp_hwinfo *hwinfo) 31 { 32 return hwinfo->version & NFP_HWINFO_VERSION_UPDATING; 33 } 34 35 static int 36 nfp_hwinfo_db_walk(struct nfp_hwinfo *hwinfo, uint32_t size) 37 { 38 const char *key, *val, *end = hwinfo->data + size; 39 40 for (key = hwinfo->data; *key && key < end; 41 key = val + strlen(val) + 1) { 42 val = key + strlen(key) + 1; 43 if (val >= end) { 44 PMD_DRV_LOG(ERR, "Bad HWINFO - overflowing value"); 45 return -EINVAL; 46 } 47 48 if (val + strlen(val) + 1 > end) { 49 PMD_DRV_LOG(ERR, "Bad HWINFO - overflowing value"); 50 return -EINVAL; 51 } 52 } 53 return 0; 54 } 55 56 static int 57 nfp_hwinfo_db_validate(struct nfp_hwinfo *db, uint32_t len) 58 { 59 uint32_t size, new_crc, *crc; 60 61 size = db->size; 62 if (size > len) { 63 PMD_DRV_LOG(ERR, "Unsupported hwinfo size %u > %u", size, len); 64 return -EINVAL; 65 } 66 67 size -= sizeof(uint32_t); 68 new_crc = nfp_crc32_posix((char *)db, size); 69 crc = (uint32_t *)(db->start + size); 70 if (new_crc != *crc) { 71 PMD_DRV_LOG(ERR, "Corrupt hwinfo table (CRC mismatch) calculated 0x%x, expected 0x%x", 72 new_crc, *crc); 73 return -EINVAL; 74 } 75 76 return nfp_hwinfo_db_walk(db, size); 77 } 78 79 static struct nfp_hwinfo * 80 nfp_hwinfo_try_fetch(struct nfp_cpp *cpp, size_t *cpp_size) 81 { 82 struct nfp_hwinfo *header; 83 void *res; 84 uint64_t cpp_addr; 85 uint32_t cpp_id; 86 int err; 87 uint8_t *db; 88 89 res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_HWINFO); 90 if (res) { 91 cpp_id = nfp_resource_cpp_id(res); 92 cpp_addr = nfp_resource_address(res); 93 *cpp_size = nfp_resource_size(res); 94 95 nfp_resource_release(res); 96 97 if (*cpp_size < HWINFO_SIZE_MIN) 98 return NULL; 99 } else { 100 return NULL; 101 } 102 103 db = malloc(*cpp_size + 1); 104 if (db == NULL) 105 return NULL; 106 107 err = nfp_cpp_read(cpp, cpp_id, cpp_addr, db, *cpp_size); 108 if (err != (int)*cpp_size) 109 goto exit_free; 110 111 header = (void *)db; 112 PMD_DRV_LOG(DEBUG, "NFP HWINFO header: %#08x", *(uint32_t *)header); 113 if (nfp_hwinfo_is_updating(header)) 114 goto exit_free; 115 116 if (header->version != NFP_HWINFO_VERSION_2) { 117 PMD_DRV_LOG(DEBUG, "Unknown HWInfo version: 0x%08x", 118 header->version); 119 goto exit_free; 120 } 121 122 /* NULL-terminate for safety */ 123 db[*cpp_size] = '\0'; 124 125 return (void *)db; 126 exit_free: 127 free(db); 128 return NULL; 129 } 130 131 static struct nfp_hwinfo * 132 nfp_hwinfo_fetch(struct nfp_cpp *cpp, size_t *hwdb_size) 133 { 134 struct timespec wait; 135 struct nfp_hwinfo *db; 136 int count; 137 138 wait.tv_sec = 0; 139 wait.tv_nsec = 10000000; 140 count = 0; 141 142 for (;;) { 143 db = nfp_hwinfo_try_fetch(cpp, hwdb_size); 144 if (db) 145 return db; 146 147 nanosleep(&wait, NULL); 148 if (count++ > 200) { 149 PMD_DRV_LOG(ERR, "NFP access error"); 150 return NULL; 151 } 152 } 153 } 154 155 struct nfp_hwinfo * 156 nfp_hwinfo_read(struct nfp_cpp *cpp) 157 { 158 struct nfp_hwinfo *db; 159 size_t hwdb_size = 0; 160 int err; 161 162 db = nfp_hwinfo_fetch(cpp, &hwdb_size); 163 if (db == NULL) 164 return NULL; 165 166 err = nfp_hwinfo_db_validate(db, hwdb_size); 167 if (err) { 168 free(db); 169 return NULL; 170 } 171 return db; 172 } 173 174 /* 175 * nfp_hwinfo_lookup() - Find a value in the HWInfo table by name 176 * @hwinfo: NFP HWinfo table 177 * @lookup: HWInfo name to search for 178 * 179 * Return: Value of the HWInfo name, or NULL 180 */ 181 const char * 182 nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup) 183 { 184 const char *key, *val, *end; 185 186 if (hwinfo == NULL || lookup == NULL) 187 return NULL; 188 189 end = hwinfo->data + hwinfo->size - sizeof(uint32_t); 190 191 for (key = hwinfo->data; *key && key < end; 192 key = val + strlen(val) + 1) { 193 val = key + strlen(key) + 1; 194 195 if (strcmp(key, lookup) == 0) 196 return val; 197 } 198 199 return NULL; 200 } 201