1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2020 Ruslan Bukin <br@bsdpad.com>
5  *
6  * This software was developed by SRI International and the University of
7  * Cambridge Computer Laboratory (Department of Computer Science and
8  * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
9  * DARPA SSITH research programme.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/bus.h>
39 #include <sys/rman.h>
40 #include <sys/kernel.h>
41 #include <sys/lock.h>
42 #include <sys/module.h>
43 #include <sys/mutex.h>
44 #include <sys/uuid.h>
45 #include <machine/bus.h>
46 
47 #include <contrib/dev/acpica/include/acpi.h>
48 #include <dev/acpica/acpivar.h>
49 
50 #include <arm64/coresight/coresight.h>
51 
52 #define	ACPI_CORESIGHT_LINK_OUTPUT	1
53 #define	ACPI_CORESIGHT_LINK_INPUT	0
54 
55 static const struct uuid acpi_graph_uuid = {
56 	0xab02a46b, 0x74c7, 0x45a2, 0xbd, 0x68,
57 	{ 0xf7, 0xd3, 0x44, 0xef, 0x21, 0x53 },
58 };
59 
60 static const struct uuid coresight_graph_uuid = {
61 	0x3ecbc8b6, 0x1d0e, 0x4fb3, 0x81, 0x07,
62 	{ 0xe6, 0x27, 0xf8, 0x05, 0xc6, 0xcd },
63 };
64 
65 static inline bool
66 cs_acpi_validate_dsd_graph(const union acpi_object *graph)
67 {
68 	const union acpi_object *rev, *nr_graphs;
69 	const union acpi_object *obj;
70 	int i, n;
71 
72 	if (graph->Package.Count < 2)
73 		return (false);
74 
75 	rev = &graph->Package.Elements[0];
76 	nr_graphs = &graph->Package.Elements[1];
77 
78 	if (rev->Type != ACPI_TYPE_INTEGER ||
79 	    nr_graphs->Type != ACPI_TYPE_INTEGER)
80 		return (false);
81 
82 	/* Revision 0 supported only. */
83 	if (rev->Integer.Value != 0)
84 		return (false);
85 
86 	/* We are looking for a single graph. */
87 	n = nr_graphs->Integer.Value;
88 	if (n != 1)
89 		return (false);
90 
91 	/* Check the number of elements. */
92 	if (graph->Package.Count != (n + 2))
93 		return (false);
94 
95 	for (i = 2; i < n + 2; i++) {
96 		obj = &graph->Package.Elements[i];
97 		if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count < 3)
98 			return (false);
99 	}
100 
101 	return (true);
102 }
103 
104 static inline bool
105 cs_is_acpi_guid(const union acpi_object *obj)
106 {
107 
108 	return (obj->Type == ACPI_TYPE_BUFFER) && (obj->Buffer.Length == 16);
109 }
110 
111 static inline bool
112 cs_guid_equal(const struct uuid *u1, const struct uuid *u2)
113 {
114 
115 	if (memcmp(u1, u2, 16) == 0)
116 		return (true);
117 
118 	return (false);
119 }
120 
121 static inline bool
122 cs_acpi_guid_matches(const union acpi_object *obj, const struct uuid *guid)
123 {
124 
125 	if (cs_is_acpi_guid(obj) &&
126 	    cs_guid_equal((struct uuid *)obj->Buffer.Pointer, guid))
127 		return (true);
128 
129 	return (false);
130 }
131 
132 static inline bool
133 is_acpi_dsd_graph_guid(const union acpi_object *obj)
134 {
135 
136 	return (cs_acpi_guid_matches(obj, &acpi_graph_uuid));
137 }
138 
139 static inline bool
140 cs_is_acpi_coresight_graph_guid(const union acpi_object *obj)
141 {
142 
143 	return (cs_acpi_guid_matches(obj, &coresight_graph_uuid));
144 }
145 
146 static inline bool
147 cs_is_acpi_coresight_graph(const union acpi_object *obj)
148 {
149 	const union acpi_object *graphid, *guid, *links;
150 
151 	if (obj->Type != ACPI_TYPE_PACKAGE ||
152 	    obj->Package.Count < 3)
153 		return (false);
154 
155 	graphid = &obj->Package.Elements[0];
156 	guid = &obj->Package.Elements[1];
157 	links = &obj->Package.Elements[2];
158 
159 	if (graphid->Type != ACPI_TYPE_INTEGER ||
160 	    links->Type != ACPI_TYPE_INTEGER)
161 		return (false);
162 
163 	if (cs_is_acpi_coresight_graph_guid(guid))
164 		return (true);
165 
166 	return (false);
167 }
168 
169 static const union acpi_object *
170 cs_get_dsd_graph(device_t dev)
171 {
172 	const union acpi_object *guid, *package;
173 	union acpi_object *dsd;
174 	ACPI_STATUS status;
175 	ACPI_BUFFER buf;
176 	device_t bus;
177 	int i;
178 
179 	buf.Length = PAGE_SIZE;
180 	buf.Pointer = malloc(buf.Length, M_TEMP, M_NOWAIT | M_ZERO);
181 	if (buf.Pointer == NULL) {
182 		printf("Failed to allocate memory.\n");
183 		return (NULL);
184 	}
185 
186 	bus = device_get_parent(dev);
187 	status = ACPI_EVALUATE_OBJECT(bus, dev, "_DSD", NULL, &buf);
188 	if (ACPI_FAILURE(status)) {
189 		printf("Failed to evaluate object.\n");
190 		return (NULL);
191 	}
192 
193 	dsd = buf.Pointer;
194 
195 	for (i = 0; i + 1 < dsd->Package.Count; i += 2) {
196 		guid = &dsd->Package.Elements[i];
197 		package = &dsd->Package.Elements[i + 1];
198 
199 		if (!cs_is_acpi_guid(guid) ||
200 		    package->Type != ACPI_TYPE_PACKAGE)
201 			break;
202 
203 		if (!is_acpi_dsd_graph_guid(guid))
204 			continue;
205 
206 		if (cs_acpi_validate_dsd_graph(package))
207 			return (package);
208 	}
209 
210 	return (NULL);
211 }
212 
213 static inline bool
214 cs_acpi_validate_coresight_graph(const union acpi_object *cs_graph)
215 {
216 	int nlinks;
217 
218 	nlinks = cs_graph->Package.Elements[2].Integer.Value;
219 	if (cs_graph->Package.Count != (nlinks + 3))
220 		return (false);
221 
222 	return (true);
223 }
224 
225 static const union acpi_object *
226 cs_get_coresight_graph(device_t dev)
227 {
228 	const union acpi_object *graph_list, *graph;
229 	int i, nr_graphs;
230 
231 	graph_list = cs_get_dsd_graph(dev);
232 	if (!graph_list) {
233 		printf("failed to get graph list\n");
234 		return (NULL);
235 	}
236 
237 	nr_graphs = graph_list->Package.Elements[1].Integer.Value;
238 	for (i = 2; i < nr_graphs + 2; i++) {
239 		graph = &graph_list->Package.Elements[i];
240 		if (!cs_is_acpi_coresight_graph(graph))
241 			continue;
242 		if (cs_acpi_validate_coresight_graph(graph))
243 			return (graph);
244 		break;
245 	}
246 
247 	return (NULL);
248 }
249 
250 static int
251 cs_acpi_record_endpoint(device_t dev,
252     struct coresight_platform_data *pdata,
253     const union acpi_object *link)
254 {
255 	const union acpi_object *fields;
256 	struct endpoint *endp;
257 	ACPI_HANDLE handle;
258 	int dir;
259 
260 	if (link->Type != ACPI_TYPE_PACKAGE ||
261 	    link->Package.Count != 4)
262 		return (ENXIO);
263 
264 	fields = link->Package.Elements;
265 	if (fields[0].Type != ACPI_TYPE_INTEGER ||
266 	    fields[1].Type != ACPI_TYPE_INTEGER ||
267 	    fields[2].Type != ACPI_TYPE_LOCAL_REFERENCE ||
268 	    fields[3].Type != ACPI_TYPE_INTEGER)
269 		return (ENXIO);
270 
271 	handle = fields[2].Reference.Handle;
272 	dir = fields[3].Integer.Value;
273 
274 	endp = malloc(sizeof(struct endpoint),
275 	    M_CORESIGHT, M_WAITOK | M_ZERO);
276 	if (endp == NULL) {
277 		device_printf(dev, "Failed to allocate memory.\n");
278 		return (ENXIO);
279 	}
280 
281 	endp->their_handle = handle;
282 	endp->my_handle = acpi_get_handle(dev);
283 
284 	mtx_lock(&pdata->mtx_lock);
285 	TAILQ_INSERT_TAIL(&pdata->endpoints, endp, link);
286 	mtx_unlock(&pdata->mtx_lock);
287 
288 	if (dir == ACPI_CORESIGHT_LINK_OUTPUT) {
289 		pdata->out_ports++;
290 	} else {
291 		endp->input = true;
292 		pdata->in_ports++;
293 	}
294 
295 	return (0);
296 }
297 
298 static int
299 coresight_acpi_get_ports(device_t dev,
300     struct coresight_platform_data *pdata)
301 {
302 	const union acpi_object *graph;
303 	const union acpi_object *link;
304 	int nlinks;
305 	int error;
306 	int i;
307 
308 	graph = cs_get_coresight_graph(dev);
309 	if (graph == NULL) {
310 		device_printf(dev, "Coresight graph not found.\n");
311 		return (ENXIO);
312 	}
313 
314 	nlinks = graph->Package.Elements[2].Integer.Value;
315 	if (!nlinks)
316 		return (0);
317 
318 	for (i = 0; i < nlinks; i++) {
319 		link = &graph->Package.Elements[3 + i];
320 		error = cs_acpi_record_endpoint(dev, pdata, link);
321 		if (error < 0)
322 			return (error);
323 	}
324 
325 	return (0);
326 }
327 
328 static int
329 coresight_acpi_get_cpu(device_t dev, struct coresight_platform_data *pdata)
330 {
331 	ACPI_HANDLE handle, parent;
332 	ACPI_STATUS status;
333 	int cpuid;
334 
335 	handle = acpi_get_handle(dev);
336 
337 	status = AcpiGetParent(handle, &parent);
338 	if (!ACPI_SUCCESS(status))
339 		return (ENXIO);
340 
341 	if (!acpi_MatchHid(parent, "ACPI0007"))
342 		return (ENXIO);
343 
344 	status = acpi_GetInteger(parent, "_UID", &cpuid);
345 	if (ACPI_SUCCESS(status)) {
346 		pdata->cpu = cpuid;
347 		return (0);
348 	}
349 
350 	return (ENXIO);
351 }
352 
353 struct coresight_platform_data *
354 coresight_acpi_get_platform_data(device_t dev)
355 {
356 	struct coresight_platform_data *pdata;
357 
358 	pdata = malloc(sizeof(struct coresight_platform_data),
359 	    M_CORESIGHT, M_WAITOK | M_ZERO);
360 	pdata->bus_type = CORESIGHT_BUS_ACPI;
361 
362 	mtx_init(&pdata->mtx_lock, "Coresight Platform Data", NULL, MTX_DEF);
363 	TAILQ_INIT(&pdata->endpoints);
364 
365 	coresight_acpi_get_cpu(dev, pdata);
366 	coresight_acpi_get_ports(dev, pdata);
367 
368 	if (bootverbose)
369 		printf("Total ports: in %d out %d\n",
370 		    pdata->in_ports, pdata->out_ports);
371 
372 	return (pdata);
373 }
374