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 
52 static const struct uuid acpi_graph_uuid = {
53 	0xab02a46b, 0x74c7, 0x45a2, 0xbd, 0x68,
54 	{ 0xf7, 0xd3, 0x44, 0xef, 0x21, 0x53 },
55 };
56 
57 static const struct uuid coresight_graph_uuid = {
58 	0x3ecbc8b6, 0x1d0e, 0x4fb3, 0x81, 0x07,
59 	{ 0xe6, 0x27, 0xf8, 0x05, 0xc6, 0xcd },
60 };
61 
62 static inline bool
63 cs_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 
101 static inline bool
102 cs_is_acpi_guid(const union acpi_object *obj)
103 {
104 
105 	return (obj->Type == ACPI_TYPE_BUFFER) && (obj->Buffer.Length == 16);
106 }
107 
108 static inline bool
109 cs_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 
118 static inline bool
119 cs_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 
129 static inline bool
130 is_acpi_dsd_graph_guid(const union acpi_object *obj)
131 {
132 
133 	return (cs_acpi_guid_matches(obj, &acpi_graph_uuid));
134 }
135 
136 static inline bool
137 cs_is_acpi_coresight_graph_guid(const union acpi_object *obj)
138 {
139 
140 	return (cs_acpi_guid_matches(obj, &coresight_graph_uuid));
141 }
142 
143 static inline bool
144 cs_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 
166 static const union acpi_object *
167 cs_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 
210 static inline bool
211 cs_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 
222 static const union acpi_object *
223 cs_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 
247 static int
248 cs_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 
295 static int
296 coresight_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 
325 static int
326 coresight_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 
350 struct coresight_platform_data *
351 coresight_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