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
55static const struct uuid acpi_graph_uuid = {
56	0xab02a46b, 0x74c7, 0x45a2, 0xbd, 0x68,
57	{ 0xf7, 0xd3, 0x44, 0xef, 0x21, 0x53 },
58};
59
60static const struct uuid coresight_graph_uuid = {
61	0x3ecbc8b6, 0x1d0e, 0x4fb3, 0x81, 0x07,
62	{ 0xe6, 0x27, 0xf8, 0x05, 0xc6, 0xcd },
63};
64
65static inline bool
66cs_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
104static inline bool
105cs_is_acpi_guid(const union acpi_object *obj)
106{
107
108	return (obj->Type == ACPI_TYPE_BUFFER) && (obj->Buffer.Length == 16);
109}
110
111static inline bool
112cs_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
121static inline bool
122cs_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
132static inline bool
133is_acpi_dsd_graph_guid(const union acpi_object *obj)
134{
135
136	return (cs_acpi_guid_matches(obj, &acpi_graph_uuid));
137}
138
139static inline bool
140cs_is_acpi_coresight_graph_guid(const union acpi_object *obj)
141{
142
143	return (cs_acpi_guid_matches(obj, &coresight_graph_uuid));
144}
145
146static inline bool
147cs_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
169static const union acpi_object *
170cs_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
213static inline bool
214cs_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
225static const union acpi_object *
226cs_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
250static int
251cs_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
298static int
299coresight_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
328static int
329coresight_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
353struct coresight_platform_data *
354coresight_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