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