1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Core driver model support for ACPI table generation
4  *
5  * Copyright 2019 Google LLC
6  * Written by Simon Glass <sjg@chromium.org>
7  */
8 
9 #define LOG_CATEOGRY	LOGC_ACPI
10 
11 #include <display_options.h>
12 #include <dm.h>
13 #include <log.h>
14 #include <malloc.h>
15 #include <mapmem.h>
16 #include <acpi/acpi_device.h>
17 #include <dm/acpi.h>
18 #include <dm/device-internal.h>
19 #include <dm/root.h>
20 
21 #define MAX_ACPI_ITEMS	100
22 
23 /**
24  * Type of table that we collected
25  *
26  * @TYPE_NONE: Not yet known
27  * @TYPE_SSDT: Items in the Secondary System Description Table
28  * @TYPE_DSDT: Items in the Differentiated System Description Table
29  * @TYPE_OTHER: Other (whole)
30  */
31 enum gen_type_t {
32 	TYPE_NONE,
33 	TYPE_SSDT,
34 	TYPE_DSDT,
35 	TYPE_OTHER,
36 };
37 
38 const char *gen_type_str[] = {
39 	"-",
40 	"ssdt",
41 	"dsdt",
42 	"other",
43 };
44 
45 /* Type of method to call */
46 enum method_t {
47 	METHOD_WRITE_TABLES,
48 	METHOD_FILL_SSDT,
49 	METHOD_INJECT_DSDT,
50 	METHOD_SETUP_NHLT,
51 	METHOD_FILL_MADT,
52 };
53 
54 /* Prototype for all methods */
55 typedef int (*acpi_method)(const struct udevice *dev, struct acpi_ctx *ctx);
56 
57 /**
58  * struct acpi_item - Holds info about ACPI data generated by a driver method
59  *
60  * @dev: Device that generated this data
61  * @type: Table type it refers to
62  * @writer: Writer that wrote this table
63  * @base: Pointer to base of table in its original location
64  * @buf: Buffer allocated to contain the data (NULL if not allocated)
65  * @size: Size of the data in bytes
66  */
67 struct acpi_item {
68 	struct udevice *dev;
69 	const struct acpi_writer *writer;
70 	enum gen_type_t type;
71 	const char *base;
72 	char *buf;
73 	int size;
74 };
75 
76 /* List of ACPI items collected */
77 static struct acpi_item acpi_item[MAX_ACPI_ITEMS];
78 static int item_count;
79 
acpi_copy_name(char * out_name,const char * name)80 int acpi_copy_name(char *out_name, const char *name)
81 {
82 	strncpy(out_name, name, ACPI_NAME_LEN);
83 	out_name[ACPI_NAME_LEN] = '\0';
84 
85 	return 0;
86 }
87 
acpi_get_name(const struct udevice * dev,char * out_name)88 int acpi_get_name(const struct udevice *dev, char *out_name)
89 {
90 	struct acpi_ops *aops;
91 	const char *name;
92 	int ret;
93 
94 	aops = device_get_acpi_ops(dev);
95 	if (aops && aops->get_name)
96 		return aops->get_name(dev, out_name);
97 	name = dev_read_string(dev, "acpi,name");
98 	if (name)
99 		return acpi_copy_name(out_name, name);
100 	ret = acpi_device_infer_name(dev, out_name);
101 	if (ret)
102 		return log_msg_ret("dev", ret);
103 
104 	return 0;
105 }
106 
acpi_get_path(const struct udevice * dev,char * out_path,int maxlen)107 int acpi_get_path(const struct udevice *dev, char *out_path, int maxlen)
108 {
109 	const char *path;
110 	int ret;
111 
112 	path = dev_read_string(dev, "acpi,path");
113 	if (path) {
114 		if (strlen(path) >= maxlen)
115 			return -ENOSPC;
116 		strcpy(out_path, path);
117 		return 0;
118 	}
119 	ret = acpi_device_path(dev, out_path, maxlen);
120 	if (ret)
121 		return log_msg_ret("dev", ret);
122 
123 	return 0;
124 }
125 
126 /**
127  * add_item() - Add a new item to the list of data collected
128  *
129  * @ctx: ACPI context
130  * @dev: Device that generated the data, if type != TYPE_OTHER
131  * @writer: Writer entry that generated the data, if type == TYPE_OTHER
132  * @type: Table type it refers to
133  * @start: The start of the data (the end is obtained from ctx->current)
134  * Return: 0 if OK, -ENOSPC if too many items, -ENOMEM if out of memory
135  */
add_item(struct acpi_ctx * ctx,struct udevice * dev,const struct acpi_writer * writer,enum gen_type_t type,void * start)136 static int add_item(struct acpi_ctx *ctx, struct udevice *dev,
137 		    const struct acpi_writer *writer, enum gen_type_t type,
138 		    void *start)
139 {
140 	struct acpi_item *item;
141 	void *end = ctx->current;
142 
143 	if (item_count == MAX_ACPI_ITEMS) {
144 		log_err("Too many items\n");
145 		return log_msg_ret("mem", -ENOSPC);
146 	}
147 
148 	item = &acpi_item[item_count];
149 	item->dev = dev;
150 	item->writer = writer;
151 	item->type = type;
152 	item->size = end - start;
153 	item->base = start;
154 	if (!item->size)
155 		return 0;
156 	if (type != TYPE_OTHER) {
157 		item->buf = malloc(item->size);
158 		if (!item->buf)
159 			return log_msg_ret("mem", -ENOMEM);
160 		memcpy(item->buf, start, item->size);
161 	}
162 	item_count++;
163 	log_debug("* %s: Added type %d, %p, size %x\n",
164 		  dev ? dev->name : "other", type, start, item->size);
165 
166 	return 0;
167 }
168 
acpi_add_other_item(struct acpi_ctx * ctx,const struct acpi_writer * writer,void * start)169 int acpi_add_other_item(struct acpi_ctx *ctx, const struct acpi_writer *writer,
170 			void *start)
171 {
172 	return add_item(ctx, NULL, writer, TYPE_OTHER, start);
173 }
174 
acpi_dump_items(enum acpi_dump_option option)175 void acpi_dump_items(enum acpi_dump_option option)
176 {
177 	int i;
178 
179 	printf("Seq  Type       Base   Size  Device/Writer\n");
180 	printf("---  -----  --------   ----  -------------\n");
181 	for (i = 0; i < item_count; i++) {
182 		struct acpi_item *item = &acpi_item[i];
183 
184 		printf("%3x  %-5s  %8lx  %5x  %s\n", i,
185 		       gen_type_str[item->type],
186 		       (ulong)map_to_sysmem(item->base), item->size,
187 		       item->dev ? item->dev->name : item->writer->name);
188 		if (option == ACPI_DUMP_CONTENTS) {
189 			print_buffer(0, item->buf ? item->buf : item->base, 1,
190 				     item->size, 0);
191 			printf("\n");
192 		}
193 	}
194 }
195 
find_acpi_item(const char * devname)196 static struct acpi_item *find_acpi_item(const char *devname)
197 {
198 	int i;
199 
200 	for (i = 0; i < item_count; i++) {
201 		struct acpi_item *item = &acpi_item[i];
202 
203 		if (item->dev && !strcmp(devname, item->dev->name))
204 			return item;
205 	}
206 
207 	return NULL;
208 }
209 
210 /**
211  * sort_acpi_item_type - Sort the ACPI items into the desired order
212  *
213  * This looks up the ordering in the device tree and then adds each item one by
214  * one into the supplied buffer
215  *
216  * @ctx: ACPI context
217  * @start: Start position to put the sorted items. The items will follow each
218  *	other in sorted order
219  * @type: Type of items to sort
220  * Return: 0 if OK, -ve on error
221  */
sort_acpi_item_type(struct acpi_ctx * ctx,void * start,enum gen_type_t type)222 static int sort_acpi_item_type(struct acpi_ctx *ctx, void *start,
223 			       enum gen_type_t type)
224 {
225 	const u32 *order;
226 	int size;
227 	int count;
228 	void *ptr;
229 	void *end = ctx->current;
230 
231 	ptr = start;
232 	order = ofnode_read_chosen_prop(type == TYPE_DSDT ?
233 					"u-boot,acpi-dsdt-order" :
234 					"u-boot,acpi-ssdt-order", &size);
235 	if (!order) {
236 		log_debug("Failed to find ordering, leaving as is\n");
237 		return 0;
238 	}
239 
240 	/*
241 	 * This algorithm rewrites the context buffer without changing its
242 	 * length. So there is no need to update ctx-current
243 	 */
244 	count = size / sizeof(u32);
245 	while (count--) {
246 		struct acpi_item *item;
247 		const char *name;
248 		ofnode node;
249 
250 		node = ofnode_get_by_phandle(fdt32_to_cpu(*order++));
251 		name = ofnode_get_name(node);
252 		item = find_acpi_item(name);
253 		if (!item) {
254 			log_err("Failed to find item '%s'\n", name);
255 			return log_msg_ret("find", -ENOENT);
256 		}
257 		if (item->type == type) {
258 			log_debug("   - add %s\n", item->dev->name);
259 			memcpy(ptr, item->buf, item->size);
260 			ptr += item->size;
261 		}
262 	}
263 
264 	/*
265 	 * If the sort order is missing an item then the output will be too
266 	 * small. Report this error since the item needs to be added to the
267 	 * ordering for the ACPI tables to be complete.
268 	 */
269 	if (ptr != end) {
270 		log_warning("*** Missing bytes: ptr=%p, end=%p\n", ptr, end);
271 		return -ENXIO;
272 	}
273 
274 	return 0;
275 }
276 
acpi_get_method(struct udevice * dev,enum method_t method)277 acpi_method acpi_get_method(struct udevice *dev, enum method_t method)
278 {
279 	struct acpi_ops *aops;
280 
281 	aops = device_get_acpi_ops(dev);
282 	if (aops) {
283 		switch (method) {
284 		case METHOD_WRITE_TABLES:
285 			return aops->write_tables;
286 		case METHOD_FILL_MADT:
287 			return aops->fill_madt;
288 		case METHOD_FILL_SSDT:
289 			return aops->fill_ssdt;
290 		case METHOD_INJECT_DSDT:
291 			return aops->inject_dsdt;
292 		case METHOD_SETUP_NHLT:
293 			return aops->setup_nhlt;
294 		}
295 	}
296 
297 	return NULL;
298 }
299 
acpi_recurse_method(struct acpi_ctx * ctx,struct udevice * parent,enum method_t method,enum gen_type_t type)300 int acpi_recurse_method(struct acpi_ctx *ctx, struct udevice *parent,
301 			enum method_t method, enum gen_type_t type)
302 {
303 	struct udevice *dev;
304 	acpi_method func;
305 	int ret;
306 
307 	func = acpi_get_method(parent, method);
308 	if (func) {
309 		log_debug("- method %d, %s %p\n", method, parent->name, func);
310 		ret = device_of_to_plat(parent);
311 		if (ret)
312 			return log_msg_ret("ofdata", ret);
313 		ctx->tab_start = ctx->current;
314 		ret = func(parent, ctx);
315 		if (ret)
316 			return log_msg_ret("func", ret);
317 
318 		/* Add the item to the internal list */
319 		if (type != TYPE_NONE) {
320 			ret = add_item(ctx, parent, NULL, type, ctx->tab_start);
321 			if (ret)
322 				return log_msg_ret("add", ret);
323 		}
324 	}
325 	device_foreach_child(dev, parent) {
326 		ret = acpi_recurse_method(ctx, dev, method, type);
327 		if (ret)
328 			return log_msg_ret("recurse", ret);
329 	}
330 
331 	return 0;
332 }
333 
acpi_fill_madt_subtbl(struct acpi_ctx * ctx)334 int acpi_fill_madt_subtbl(struct acpi_ctx *ctx)
335 {
336 	int ret;
337 
338 	log_debug("Writing MADT table\n");
339 	ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_MADT, TYPE_NONE);
340 	log_debug("Writing MADT finished, err=%d\n", ret);
341 	if (ret)
342 		return log_msg_ret("build", ret);
343 
344 	return ret;
345 }
346 
acpi_fill_ssdt(struct acpi_ctx * ctx)347 int acpi_fill_ssdt(struct acpi_ctx *ctx)
348 {
349 	void *start = ctx->current;
350 	int ret;
351 
352 	log_debug("Writing SSDT tables\n");
353 	ret = acpi_recurse_method(ctx, dm_root(), METHOD_FILL_SSDT, TYPE_SSDT);
354 	log_debug("Writing SSDT finished, err=%d\n", ret);
355 	ret = sort_acpi_item_type(ctx, start, TYPE_SSDT);
356 	if (ret)
357 		return log_msg_ret("build", ret);
358 
359 	return ret;
360 }
361 
acpi_inject_dsdt(struct acpi_ctx * ctx)362 int acpi_inject_dsdt(struct acpi_ctx *ctx)
363 {
364 	void *start = ctx->current;
365 	int ret;
366 
367 	log_debug("Writing DSDT tables\n");
368 	ret = acpi_recurse_method(ctx, dm_root(), METHOD_INJECT_DSDT,
369 				  TYPE_DSDT);
370 	log_debug("Writing DSDT finished, err=%d\n", ret);
371 	ret = sort_acpi_item_type(ctx, start, TYPE_DSDT);
372 	if (ret)
373 		return log_msg_ret("build", ret);
374 
375 	return ret;
376 }
377 
acpi_reset_items(void)378 void acpi_reset_items(void)
379 {
380 	item_count = 0;
381 }
382 
acpi_write_dev_tables(struct acpi_ctx * ctx)383 int acpi_write_dev_tables(struct acpi_ctx *ctx)
384 {
385 	int ret;
386 
387 	log_debug("Writing device tables\n");
388 	ret = acpi_recurse_method(ctx, dm_root(), METHOD_WRITE_TABLES,
389 				  TYPE_NONE);
390 	log_debug("Writing finished, err=%d\n", ret);
391 
392 	return ret;
393 }
394 
acpi_setup_nhlt(struct acpi_ctx * ctx,struct nhlt * nhlt)395 int acpi_setup_nhlt(struct acpi_ctx *ctx, struct nhlt *nhlt)
396 {
397 	int ret;
398 
399 	log_debug("Setup NHLT\n");
400 	ctx->nhlt = nhlt;
401 	ret = acpi_recurse_method(ctx, dm_root(), METHOD_SETUP_NHLT, TYPE_NONE);
402 	log_debug("Setup finished, err=%d\n", ret);
403 
404 	return ret;
405 }
406