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/param.h>
34#include <sys/systm.h>
35#include <sys/bus.h>
36#include <sys/rman.h>
37#include <sys/kernel.h>
38#include <sys/lock.h>
39#include <sys/module.h>
40#include <sys/mutex.h>
41#include <sys/uuid.h>
42#include <machine/bus.h>
43
44#include <contrib/dev/acpica/include/acpi.h>
45#include <dev/acpica/acpivar.h>
46
47#include <arm64/coresight/coresight.h>
48
49#define	ACPI_CORESIGHT_LINK_OUTPUT	1
50#define	ACPI_CORESIGHT_LINK_INPUT	0
51
52static const struct uuid acpi_graph_uuid = {
53	0xab02a46b, 0x74c7, 0x45a2, 0xbd, 0x68,
54	{ 0xf7, 0xd3, 0x44, 0xef, 0x21, 0x53 },
55};
56
57static const struct uuid coresight_graph_uuid = {
58	0x3ecbc8b6, 0x1d0e, 0x4fb3, 0x81, 0x07,
59	{ 0xe6, 0x27, 0xf8, 0x05, 0xc6, 0xcd },
60};
61
62static inline bool
63cs_acpi_validate_dsd_graph(const union acpi_object *graph)
64{
65	const union acpi_object *rev, *nr_graphs;
66	const union acpi_object *obj;
67	int i, n;
68
69	if (graph->Package.Count < 2)
70		return (false);
71
72	rev = &graph->Package.Elements[0];
73	nr_graphs = &graph->Package.Elements[1];
74
75	if (rev->Type != ACPI_TYPE_INTEGER ||
76	    nr_graphs->Type != ACPI_TYPE_INTEGER)
77		return (false);
78
79	/* Revision 0 supported only. */
80	if (rev->Integer.Value != 0)
81		return (false);
82
83	/* We are looking for a single graph. */
84	n = nr_graphs->Integer.Value;
85	if (n != 1)
86		return (false);
87
88	/* Check the number of elements. */
89	if (graph->Package.Count != (n + 2))
90		return (false);
91
92	for (i = 2; i < n + 2; i++) {
93		obj = &graph->Package.Elements[i];
94		if (obj->Type != ACPI_TYPE_PACKAGE || obj->Package.Count < 3)
95			return (false);
96	}
97
98	return (true);
99}
100
101static inline bool
102cs_is_acpi_guid(const union acpi_object *obj)
103{
104
105	return (obj->Type == ACPI_TYPE_BUFFER) && (obj->Buffer.Length == 16);
106}
107
108static inline bool
109cs_guid_equal(const struct uuid *u1, const struct uuid *u2)
110{
111
112	if (memcmp(u1, u2, 16) == 0)
113		return (true);
114
115	return (false);
116}
117
118static inline bool
119cs_acpi_guid_matches(const union acpi_object *obj, const struct uuid *guid)
120{
121
122	if (cs_is_acpi_guid(obj) &&
123	    cs_guid_equal((struct uuid *)obj->Buffer.Pointer, guid))
124		return (true);
125
126	return (false);
127}
128
129static inline bool
130is_acpi_dsd_graph_guid(const union acpi_object *obj)
131{
132
133	return (cs_acpi_guid_matches(obj, &acpi_graph_uuid));
134}
135
136static inline bool
137cs_is_acpi_coresight_graph_guid(const union acpi_object *obj)
138{
139
140	return (cs_acpi_guid_matches(obj, &coresight_graph_uuid));
141}
142
143static inline bool
144cs_is_acpi_coresight_graph(const union acpi_object *obj)
145{
146	const union acpi_object *graphid, *guid, *links;
147
148	if (obj->Type != ACPI_TYPE_PACKAGE ||
149	    obj->Package.Count < 3)
150		return (false);
151
152	graphid = &obj->Package.Elements[0];
153	guid = &obj->Package.Elements[1];
154	links = &obj->Package.Elements[2];
155
156	if (graphid->Type != ACPI_TYPE_INTEGER ||
157	    links->Type != ACPI_TYPE_INTEGER)
158		return (false);
159
160	if (cs_is_acpi_coresight_graph_guid(guid))
161		return (true);
162
163	return (false);
164}
165
166static const union acpi_object *
167cs_get_dsd_graph(device_t dev)
168{
169	const union acpi_object *guid, *package;
170	union acpi_object *dsd;
171	ACPI_STATUS status;
172	ACPI_BUFFER buf;
173	device_t bus;
174	int i;
175
176	buf.Length = PAGE_SIZE;
177	buf.Pointer = malloc(buf.Length, M_TEMP, M_NOWAIT | M_ZERO);
178	if (buf.Pointer == NULL) {
179		printf("Failed to allocate memory.\n");
180		return (NULL);
181	}
182
183	bus = device_get_parent(dev);
184	status = ACPI_EVALUATE_OBJECT(bus, dev, "_DSD", NULL, &buf);
185	if (ACPI_FAILURE(status)) {
186		printf("Failed to evaluate object.\n");
187		return (NULL);
188	}
189
190	dsd = buf.Pointer;
191
192	for (i = 0; i + 1 < dsd->Package.Count; i += 2) {
193		guid = &dsd->Package.Elements[i];
194		package = &dsd->Package.Elements[i + 1];
195
196		if (!cs_is_acpi_guid(guid) ||
197		    package->Type != ACPI_TYPE_PACKAGE)
198			break;
199
200		if (!is_acpi_dsd_graph_guid(guid))
201			continue;
202
203		if (cs_acpi_validate_dsd_graph(package))
204			return (package);
205	}
206
207	return (NULL);
208}
209
210static inline bool
211cs_acpi_validate_coresight_graph(const union acpi_object *cs_graph)
212{
213	int nlinks;
214
215	nlinks = cs_graph->Package.Elements[2].Integer.Value;
216	if (cs_graph->Package.Count != (nlinks + 3))
217		return (false);
218
219	return (true);
220}
221
222static const union acpi_object *
223cs_get_coresight_graph(device_t dev)
224{
225	const union acpi_object *graph_list, *graph;
226	int i, nr_graphs;
227
228	graph_list = cs_get_dsd_graph(dev);
229	if (!graph_list) {
230		printf("failed to get graph list\n");
231		return (NULL);
232	}
233
234	nr_graphs = graph_list->Package.Elements[1].Integer.Value;
235	for (i = 2; i < nr_graphs + 2; i++) {
236		graph = &graph_list->Package.Elements[i];
237		if (!cs_is_acpi_coresight_graph(graph))
238			continue;
239		if (cs_acpi_validate_coresight_graph(graph))
240			return (graph);
241		break;
242	}
243
244	return (NULL);
245}
246
247static int
248cs_acpi_record_endpoint(device_t dev,
249    struct coresight_platform_data *pdata,
250    const union acpi_object *link)
251{
252	const union acpi_object *fields;
253	struct endpoint *endp;
254	ACPI_HANDLE handle;
255	int dir;
256
257	if (link->Type != ACPI_TYPE_PACKAGE ||
258	    link->Package.Count != 4)
259		return (ENXIO);
260
261	fields = link->Package.Elements;
262	if (fields[0].Type != ACPI_TYPE_INTEGER ||
263	    fields[1].Type != ACPI_TYPE_INTEGER ||
264	    fields[2].Type != ACPI_TYPE_LOCAL_REFERENCE ||
265	    fields[3].Type != ACPI_TYPE_INTEGER)
266		return (ENXIO);
267
268	handle = fields[2].Reference.Handle;
269	dir = fields[3].Integer.Value;
270
271	endp = malloc(sizeof(struct endpoint),
272	    M_CORESIGHT, M_WAITOK | M_ZERO);
273	if (endp == NULL) {
274		device_printf(dev, "Failed to allocate memory.\n");
275		return (ENXIO);
276	}
277
278	endp->their_handle = handle;
279	endp->my_handle = acpi_get_handle(dev);
280
281	mtx_lock(&pdata->mtx_lock);
282	TAILQ_INSERT_TAIL(&pdata->endpoints, endp, link);
283	mtx_unlock(&pdata->mtx_lock);
284
285	if (dir == ACPI_CORESIGHT_LINK_OUTPUT) {
286		pdata->out_ports++;
287	} else {
288		endp->input = true;
289		pdata->in_ports++;
290	}
291
292	return (0);
293}
294
295static int
296coresight_acpi_get_ports(device_t dev,
297    struct coresight_platform_data *pdata)
298{
299	const union acpi_object *graph;
300	const union acpi_object *link;
301	int nlinks;
302	int error;
303	int i;
304
305	graph = cs_get_coresight_graph(dev);
306	if (graph == NULL) {
307		device_printf(dev, "Coresight graph not found.\n");
308		return (ENXIO);
309	}
310
311	nlinks = graph->Package.Elements[2].Integer.Value;
312	if (!nlinks)
313		return (0);
314
315	for (i = 0; i < nlinks; i++) {
316		link = &graph->Package.Elements[3 + i];
317		error = cs_acpi_record_endpoint(dev, pdata, link);
318		if (error < 0)
319			return (error);
320	}
321
322	return (0);
323}
324
325static int
326coresight_acpi_get_cpu(device_t dev, struct coresight_platform_data *pdata)
327{
328	ACPI_HANDLE handle, parent;
329	ACPI_STATUS status;
330	int cpuid;
331
332	handle = acpi_get_handle(dev);
333
334	status = AcpiGetParent(handle, &parent);
335	if (!ACPI_SUCCESS(status))
336		return (ENXIO);
337
338	if (!acpi_MatchHid(parent, "ACPI0007"))
339		return (ENXIO);
340
341	status = acpi_GetInteger(parent, "_UID", &cpuid);
342	if (ACPI_SUCCESS(status)) {
343		pdata->cpu = cpuid;
344		return (0);
345	}
346
347	return (ENXIO);
348}
349
350struct coresight_platform_data *
351coresight_acpi_get_platform_data(device_t dev)
352{
353	struct coresight_platform_data *pdata;
354
355	pdata = malloc(sizeof(struct coresight_platform_data),
356	    M_CORESIGHT, M_WAITOK | M_ZERO);
357	pdata->bus_type = CORESIGHT_BUS_ACPI;
358
359	mtx_init(&pdata->mtx_lock, "Coresight Platform Data", NULL, MTX_DEF);
360	TAILQ_INIT(&pdata->endpoints);
361
362	coresight_acpi_get_cpu(dev, pdata);
363	coresight_acpi_get_ports(dev, pdata);
364
365	if (bootverbose)
366		printf("Total ports: in %d out %d\n",
367		    pdata->in_ports, pdata->out_ports);
368
369	return (pdata);
370}
371