xref: /dpdk/drivers/net/nfp/nfpcore/nfp_hwinfo.c (revision 4aa10e5dc1b0fd6cc5b1b18770ac603e2c33a66c)
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