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 "nfp6000/nfp6000.h"
24 #include "nfp_resource.h"
25 #include "nfp_hwinfo.h"
26 #include "nfp_crc.h"
27 
28 static int
nfp_hwinfo_is_updating(struct nfp_hwinfo * hwinfo)29 nfp_hwinfo_is_updating(struct nfp_hwinfo *hwinfo)
30 {
31 	return hwinfo->version & NFP_HWINFO_VERSION_UPDATING;
32 }
33 
34 static int
nfp_hwinfo_db_walk(struct nfp_hwinfo * hwinfo,uint32_t size)35 nfp_hwinfo_db_walk(struct nfp_hwinfo *hwinfo, uint32_t size)
36 {
37 	const char *key, *val, *end = hwinfo->data + size;
38 
39 	for (key = hwinfo->data; *key && key < end;
40 	     key = val + strlen(val) + 1) {
41 		val = key + strlen(key) + 1;
42 		if (val >= end) {
43 			printf("Bad HWINFO - overflowing key\n");
44 			return -EINVAL;
45 		}
46 
47 		if (val + strlen(val) + 1 > end) {
48 			printf("Bad HWINFO - overflowing value\n");
49 			return -EINVAL;
50 		}
51 	}
52 	return 0;
53 }
54 
55 static int
nfp_hwinfo_db_validate(struct nfp_hwinfo * db,uint32_t len)56 nfp_hwinfo_db_validate(struct nfp_hwinfo *db, uint32_t len)
57 {
58 	uint32_t size, new_crc, *crc;
59 
60 	size = db->size;
61 	if (size > len) {
62 		printf("Unsupported hwinfo size %u > %u\n", size, len);
63 		return -EINVAL;
64 	}
65 
66 	size -= sizeof(uint32_t);
67 	new_crc = nfp_crc32_posix((char *)db, size);
68 	crc = (uint32_t *)(db->start + size);
69 	if (new_crc != *crc) {
70 		printf("Corrupt hwinfo table (CRC mismatch)\n");
71 		printf("\tcalculated 0x%x, expected 0x%x\n", new_crc, *crc);
72 		return -EINVAL;
73 	}
74 
75 	return nfp_hwinfo_db_walk(db, size);
76 }
77 
78 static struct nfp_hwinfo *
nfp_hwinfo_try_fetch(struct nfp_cpp * cpp,size_t * cpp_size)79 nfp_hwinfo_try_fetch(struct nfp_cpp *cpp, size_t *cpp_size)
80 {
81 	struct nfp_hwinfo *header;
82 	void *res;
83 	uint64_t cpp_addr;
84 	uint32_t cpp_id;
85 	int err;
86 	uint8_t *db;
87 
88 	res = nfp_resource_acquire(cpp, NFP_RESOURCE_NFP_HWINFO);
89 	if (res) {
90 		cpp_id = nfp_resource_cpp_id(res);
91 		cpp_addr = nfp_resource_address(res);
92 		*cpp_size = nfp_resource_size(res);
93 
94 		nfp_resource_release(res);
95 
96 		if (*cpp_size < HWINFO_SIZE_MIN)
97 			return NULL;
98 	} else {
99 		return NULL;
100 	}
101 
102 	db = malloc(*cpp_size + 1);
103 	if (!db)
104 		return NULL;
105 
106 	err = nfp_cpp_read(cpp, cpp_id, cpp_addr, db, *cpp_size);
107 	if (err != (int)*cpp_size)
108 		goto exit_free;
109 
110 	header = (void *)db;
111 	printf("NFP HWINFO header: %08x\n", *(uint32_t *)header);
112 	if (nfp_hwinfo_is_updating(header))
113 		goto exit_free;
114 
115 	if (header->version != NFP_HWINFO_VERSION_2) {
116 		printf("Unknown HWInfo version: 0x%08x\n",
117 			header->version);
118 		goto exit_free;
119 	}
120 
121 	/* NULL-terminate for safety */
122 	db[*cpp_size] = '\0';
123 
124 	return (void *)db;
125 exit_free:
126 	free(db);
127 	return NULL;
128 }
129 
130 static struct nfp_hwinfo *
nfp_hwinfo_fetch(struct nfp_cpp * cpp,size_t * hwdb_size)131 nfp_hwinfo_fetch(struct nfp_cpp *cpp, size_t *hwdb_size)
132 {
133 	struct timespec wait;
134 	struct nfp_hwinfo *db;
135 	int count;
136 
137 	wait.tv_sec = 0;
138 	wait.tv_nsec = 10000000;
139 	count = 0;
140 
141 	for (;;) {
142 		db = nfp_hwinfo_try_fetch(cpp, hwdb_size);
143 		if (db)
144 			return db;
145 
146 		nanosleep(&wait, NULL);
147 		if (count++ > 200) {
148 			printf("NFP access error\n");
149 			return NULL;
150 		}
151 	}
152 }
153 
154 struct nfp_hwinfo *
nfp_hwinfo_read(struct nfp_cpp * cpp)155 nfp_hwinfo_read(struct nfp_cpp *cpp)
156 {
157 	struct nfp_hwinfo *db;
158 	size_t hwdb_size = 0;
159 	int err;
160 
161 	db = nfp_hwinfo_fetch(cpp, &hwdb_size);
162 	if (!db)
163 		return NULL;
164 
165 	err = nfp_hwinfo_db_validate(db, hwdb_size);
166 	if (err) {
167 		free(db);
168 		return NULL;
169 	}
170 	return db;
171 }
172 
173 /*
174  * nfp_hwinfo_lookup() - Find a value in the HWInfo table by name
175  * @hwinfo:	NFP HWinfo table
176  * @lookup:	HWInfo name to search for
177  *
178  * Return: Value of the HWInfo name, or NULL
179  */
180 const char *
nfp_hwinfo_lookup(struct nfp_hwinfo * hwinfo,const char * lookup)181 nfp_hwinfo_lookup(struct nfp_hwinfo *hwinfo, const char *lookup)
182 {
183 	const char *key, *val, *end;
184 
185 	if (!hwinfo || !lookup)
186 		return NULL;
187 
188 	end = hwinfo->data + hwinfo->size - sizeof(uint32_t);
189 
190 	for (key = hwinfo->data; *key && key < end;
191 	     key = val + strlen(val) + 1) {
192 		val = key + strlen(key) + 1;
193 
194 		if (strcmp(key, lookup) == 0)
195 			return val;
196 	}
197 
198 	return NULL;
199 }
200