xref: /freebsd/usr.sbin/acpi/acpidb/acpidb.c (revision b1f9167f94059fd55c630891d359bcff987bd7eb)
1 /*-
2  * Copyright (c) 2000-2002 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/queue.h>
31 #include <sys/mman.h>
32 #include <sys/stat.h>
33 #include <sys/stdint.h>
34 #include <sys/types.h>
35 
36 #include <assert.h>
37 #include <ctype.h>
38 #include <err.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <unistd.h>
44 
45 #include <contrib/dev/acpica/include/acpi.h>
46 #include <contrib/dev/acpica/include/accommon.h>
47 #include <contrib/dev/acpica/include/acdebug.h>
48 
49 /*
50  * Dummy DSDT Table Header
51  */
52 
53 static ACPI_TABLE_HEADER dummy_dsdt_table = {
54 	"DSDT", 123, 1, 123, "OEMID", "OEMTBLID", 1, "CRID", 1
55 };
56 
57 /*
58  * Region space I/O routines on virtual machine
59  */
60 
61 static int	aml_debug_prompt = 1;
62 
63 struct ACPIRegionContent {
64 	TAILQ_ENTRY(ACPIRegionContent) links;
65 	int			regtype;
66 	ACPI_PHYSICAL_ADDRESS	addr;
67 	UINT8			value;
68 };
69 
70 TAILQ_HEAD(ACPIRegionContentList, ACPIRegionContent);
71 static struct	ACPIRegionContentList RegionContentList;
72 
73 static int		 aml_simulation_initialized = 0;
74 
75 ACPI_PHYSICAL_ADDRESS	 AeLocalGetRootPointer(void);
76 void			 AeTableOverride(ACPI_TABLE_HEADER *, ACPI_TABLE_HEADER **);
77 
78 static void		 aml_simulation_init(void);
79 static int		 aml_simulate_regcontent_add(int regtype,
80 			     ACPI_PHYSICAL_ADDRESS addr,
81 			     UINT8 value);
82 static int		 aml_simulate_regcontent_read(int regtype,
83 			     ACPI_PHYSICAL_ADDRESS addr,
84 			     UINT8 *valuep);
85 static int		 aml_simulate_regcontent_write(int regtype,
86 			     ACPI_PHYSICAL_ADDRESS addr,
87 			     UINT8 *valuep);
88 static UINT64		 aml_simulate_prompt(char *msg, UINT64 def_val);
89 static void		 aml_simulation_regload(const char *dumpfile);
90 static void		 aml_simulation_regdump(const char *dumpfile);
91 
92 /* Stubs to simplify linkage to the ACPI CA core subsystem. */
93 ACPI_PHYSICAL_ADDRESS
94 AeLocalGetRootPointer(void)
95 {
96 
97 	return (0);
98 }
99 
100 void
101 AeTableOverride(ACPI_TABLE_HEADER *ExistingTable, ACPI_TABLE_HEADER **NewTable)
102 {
103 }
104 
105 static void
106 aml_simulation_init(void)
107 {
108 
109 	aml_simulation_initialized = 1;
110 	TAILQ_INIT(&RegionContentList);
111 	aml_simulation_regload("region.ini");
112 }
113 
114 static int
115 aml_simulate_regcontent_add(int regtype, ACPI_PHYSICAL_ADDRESS addr, UINT8 value)
116 {
117 	struct	ACPIRegionContent *rc;
118 
119 	rc = malloc(sizeof(struct ACPIRegionContent));
120 	if (rc == NULL) {
121 		return (-1);	/* malloc fail */
122 	}
123 	rc->regtype = regtype;
124 	rc->addr = addr;
125 	rc->value = value;
126 
127 	TAILQ_INSERT_TAIL(&RegionContentList, rc, links);
128 	return (0);
129 }
130 
131 static int
132 aml_simulate_regcontent_read(int regtype, ACPI_PHYSICAL_ADDRESS addr, UINT8 *valuep)
133 {
134 	struct	ACPIRegionContent *rc;
135 
136 	if (!aml_simulation_initialized) {
137 		aml_simulation_init();
138 	}
139 	TAILQ_FOREACH(rc, &RegionContentList, links) {
140 		if (rc->regtype == regtype && rc->addr == addr) {
141 			*valuep = rc->value;
142 			return (1);	/* found */
143 		}
144 	}
145 
146 	*valuep = 0;
147 	return (aml_simulate_regcontent_add(regtype, addr, *valuep));
148 }
149 
150 static int
151 aml_simulate_regcontent_write(int regtype, ACPI_PHYSICAL_ADDRESS addr, UINT8 *valuep)
152 {
153 	struct	ACPIRegionContent *rc;
154 
155 	if (!aml_simulation_initialized) {
156 		aml_simulation_init();
157 	}
158 	TAILQ_FOREACH(rc, &RegionContentList, links) {
159 		if (rc->regtype == regtype && rc->addr == addr) {
160 			rc->value = *valuep;
161 			return (1);	/* exists */
162 		}
163 	}
164 
165 	return (aml_simulate_regcontent_add(regtype, addr, *valuep));
166 }
167 
168 static UINT64
169 aml_simulate_prompt(char *msg, UINT64 def_val)
170 {
171 	char		buf[16], *ep;
172 	UINT64		val;
173 
174 	val = def_val;
175 	printf("DEBUG");
176 	if (msg != NULL) {
177 		printf("%s", msg);
178 	}
179 	printf("(default: 0x%jx ", (uintmax_t)val);
180 	printf(" / %ju) >>", (uintmax_t)val);
181 	fflush(stdout);
182 
183 	bzero(buf, sizeof buf);
184 	while (1) {
185 		if (read(0, buf, sizeof buf) == 0) {
186 			continue;
187 		}
188 		if (buf[0] == '\n') {
189 			break;	/* use default value */
190 		}
191 		if (buf[0] == '0' && buf[1] == 'x') {
192 			val = strtoq(buf, &ep, 16);
193 		} else {
194 			val = strtoq(buf, &ep, 10);
195 		}
196 		break;
197 	}
198 	return (val);
199 }
200 
201 static void
202 aml_simulation_regload(const char *dumpfile)
203 {
204 	char	buf[256], *np, *ep;
205 	struct	ACPIRegionContent rc;
206 	FILE	*fp;
207 
208 	if (!aml_simulation_initialized) {
209 		return;
210 	}
211 
212 	if ((fp = fopen(dumpfile, "r")) == NULL) {
213 		return;
214 	}
215 
216 	while (fgets(buf, sizeof buf, fp) != NULL) {
217 		np = buf;
218 		/* reading region type */
219 		rc.regtype = strtoq(np, &ep, 10);
220 		if (np == ep) {
221 			continue;
222 		}
223 		np = ep;
224 
225 		/* reading address */
226 		rc.addr = strtoq(np, &ep, 16);
227 		if (np == ep) {
228 			continue;
229 		}
230 		np = ep;
231 
232 		/* reading value */
233 		rc.value = strtoq(np, &ep, 16);
234 		if (np == ep) {
235 			continue;
236 		}
237 		aml_simulate_regcontent_write(rc.regtype, rc.addr, &rc.value);
238 	}
239 
240 	fclose(fp);
241 }
242 
243 static void
244 aml_simulation_regdump(const char *dumpfile)
245 {
246 	struct	ACPIRegionContent *rc;
247 	FILE	*fp;
248 
249 	if (!aml_simulation_initialized) {
250 		return;
251 	}
252 	if ((fp = fopen(dumpfile, "w")) == NULL) {
253 		warn("%s", dumpfile);
254 		return;
255 	}
256 	while (!TAILQ_EMPTY(&RegionContentList)) {
257 		rc = TAILQ_FIRST(&RegionContentList);
258 		fprintf(fp, "%d	0x%jx	0x%x\n",
259 		    rc->regtype, (uintmax_t)rc->addr, rc->value);
260 		TAILQ_REMOVE(&RegionContentList, rc, links);
261 		free(rc);
262 	}
263 
264 	fclose(fp);
265 	TAILQ_INIT(&RegionContentList);
266 }
267 
268 /*
269  * Space handlers on virtual machine
270  */
271 
272 static ACPI_STATUS
273 aml_vm_space_handler(
274 	UINT32			SpaceID,
275 	UINT32			Function,
276 	ACPI_PHYSICAL_ADDRESS	Address,
277 	UINT32			BitWidth,
278 	UINT64			*Value,
279 	int			Prompt)
280 {
281 	int			state;
282 	UINT8			val;
283 	UINT64			value, i;
284 	char			msg[256];
285 	static const char	*space_names[] = {
286 		"SYSTEM_MEMORY", "SYSTEM_IO", "PCI_CONFIG",
287 		"EC", "SMBUS", "CMOS", "PCI_BAR_TARGET"};
288 
289 	switch (Function) {
290 	case ACPI_READ:
291 		value = 0;
292 		for (i = 0; (i * 8) < BitWidth; i++) {
293 			state = aml_simulate_regcontent_read(SpaceID,
294 							     Address + i, &val);
295 			if (state == -1) {
296 				return (AE_NO_MEMORY);
297 			}
298 			value |= val << (i * 8);
299 		}
300 		*Value = value;
301 		if (Prompt) {
302 			sprintf(msg, "[read (%s, %2d, 0x%jx)]",
303 				space_names[SpaceID], BitWidth,
304 				(uintmax_t)Address);
305 			*Value = aml_simulate_prompt(msg, value);
306 			if (*Value != value) {
307 				return(aml_vm_space_handler(SpaceID,
308 						ACPI_WRITE,
309 						Address, BitWidth, Value, 0));
310 			}
311 		}
312 		break;
313 
314 	case ACPI_WRITE:
315 		value = *Value;
316 		if (Prompt) {
317 			sprintf(msg, "[write(%s, %2d, 0x%jx)]",
318 				space_names[SpaceID], BitWidth,
319 				(uintmax_t)Address);
320 			value = aml_simulate_prompt(msg, *Value);
321 		}
322 		*Value = value;
323 		for (i = 0; (i * 8) < BitWidth; i++) {
324 			val = value & 0xff;
325 			state = aml_simulate_regcontent_write(SpaceID,
326 							      Address + i, &val);
327 			if (state == -1) {
328 				return (AE_NO_MEMORY);
329 			}
330 			value = value >> 8;
331 		}
332 	}
333 
334 	return (AE_OK);
335 }
336 
337 #define DECLARE_VM_SPACE_HANDLER(name, id);			\
338 static ACPI_STATUS						\
339 aml_vm_space_handler_##name (					\
340 	UINT32			Function,			\
341 	ACPI_PHYSICAL_ADDRESS	Address,			\
342 	UINT32			BitWidth,			\
343 	UINT64			*Value)				\
344 {								\
345 	return (aml_vm_space_handler(id, Function, Address,	\
346 		BitWidth, Value, aml_debug_prompt));		\
347 }
348 
349 DECLARE_VM_SPACE_HANDLER(system_memory,	ACPI_ADR_SPACE_SYSTEM_MEMORY);
350 DECLARE_VM_SPACE_HANDLER(system_io,	ACPI_ADR_SPACE_SYSTEM_IO);
351 DECLARE_VM_SPACE_HANDLER(pci_config,	ACPI_ADR_SPACE_PCI_CONFIG);
352 DECLARE_VM_SPACE_HANDLER(ec,		ACPI_ADR_SPACE_EC);
353 DECLARE_VM_SPACE_HANDLER(smbus,		ACPI_ADR_SPACE_SMBUS);
354 DECLARE_VM_SPACE_HANDLER(cmos,		ACPI_ADR_SPACE_CMOS);
355 DECLARE_VM_SPACE_HANDLER(pci_bar_target,ACPI_ADR_SPACE_PCI_BAR_TARGET);
356 
357 /*
358  * Load DSDT data file and invoke debugger
359  */
360 
361 static int
362 load_dsdt(const char *dsdtfile)
363 {
364 	char			filetmp[PATH_MAX];
365 	u_int8_t		*code;
366 	struct stat		sb;
367 	int			fd, fd2;
368 	int			error;
369 
370 	fd = open(dsdtfile, O_RDONLY, 0);
371 	if (fd == -1) {
372 		perror("open");
373 		return (-1);
374 	}
375 	if (fstat(fd, &sb) == -1) {
376 		perror("fstat");
377 		close(fd);
378 		return (-1);
379 	}
380 	code = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, (off_t)0);
381 	if (code == NULL) {
382 		perror("mmap");
383 		return (-1);
384 	}
385 	if ((error = AcpiInitializeSubsystem()) != AE_OK) {
386 		return (-1);
387 	}
388 
389 	/*
390 	 * make sure DSDT data contains table header or not.
391 	 */
392 	if (strncmp((char *)code, "DSDT", 4) == 0) {
393 		strncpy(filetmp, dsdtfile, sizeof(filetmp));
394 	} else {
395 		mode_t	mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
396 		dummy_dsdt_table.Length = sizeof(ACPI_TABLE_HEADER) + sb.st_size;
397 		snprintf(filetmp, sizeof(filetmp), "%s.tmp", dsdtfile);
398 		fd2 = open(filetmp, O_WRONLY | O_CREAT | O_TRUNC, mode);
399 		if (fd2 == -1) {
400 			perror("open");
401 			return (-1);
402 		}
403 		write(fd2, &dummy_dsdt_table, sizeof(ACPI_TABLE_HEADER));
404 
405 		write(fd2, code, sb.st_size);
406 		close(fd2);
407 	}
408 
409 	/*
410 	 * Install the virtual machine version of address space handlers.
411 	 */
412 	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
413 			ACPI_ADR_SPACE_SYSTEM_MEMORY,
414 			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_system_memory,
415 			NULL, NULL)) != AE_OK) {
416 		fprintf(stderr, "could not initialise SystemMemory handler: %d\n", error);
417 		return (-1);
418 	}
419 	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
420 			ACPI_ADR_SPACE_SYSTEM_IO,
421 			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_system_io,
422 			NULL, NULL)) != AE_OK) {
423 		fprintf(stderr, "could not initialise SystemIO handler: %d\n", error);
424 		return (-1);
425 	}
426 	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
427 			ACPI_ADR_SPACE_PCI_CONFIG,
428 			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_pci_config,
429 			NULL, NULL)) != AE_OK) {
430 		fprintf(stderr, "could not initialise PciConfig handler: %d\n", error);
431 		return (-1);
432 	}
433 	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
434 			ACPI_ADR_SPACE_EC,
435 			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_ec,
436 			NULL, NULL)) != AE_OK) {
437 		fprintf(stderr, "could not initialise EC handler: %d\n", error);
438 		return (-1);
439 	}
440 	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
441 			ACPI_ADR_SPACE_SMBUS,
442 			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_smbus,
443 			NULL, NULL)) != AE_OK) {
444 		fprintf(stderr, "could not initialise SMBUS handler: %d\n", error);
445 		return (-1);
446 	}
447 	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
448 			ACPI_ADR_SPACE_CMOS,
449 			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_cmos,
450 			NULL, NULL)) != AE_OK) {
451 		fprintf(stderr, "could not initialise CMOS handler: %d\n", error);
452 		return (-1);
453 	}
454 	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
455 			ACPI_ADR_SPACE_PCI_BAR_TARGET,
456 			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_pci_bar_target,
457 			NULL, NULL)) != AE_OK) {
458 		fprintf(stderr, "could not initialise PCI BAR TARGET handler: %d\n", error);
459 		return (-1);
460 	}
461 
462 	AcpiDbGetTableFromFile(filetmp, NULL);
463 
464 	AcpiDbInitialize();
465 	AcpiGbl_DebuggerConfiguration = 0;
466 	AcpiDbUserCommands(':', NULL);
467 
468 	if (strcmp(dsdtfile, filetmp) != 0) {
469 		unlink(filetmp);
470 	}
471 
472 	return (0);
473 }
474 
475 static void
476 usage(const char *progname)
477 {
478 
479 	printf("usage: %s dsdt_file\n", progname);
480 	exit(1);
481 }
482 
483 int
484 main(int argc, char *argv[])
485 {
486 	char	*progname;
487 
488 	progname = argv[0];
489 
490 	if (argc == 1) {
491 		usage(progname);
492 	}
493 
494 	AcpiDbgLevel = ACPI_DEBUG_DEFAULT;
495 
496 	/*
497 	 * Match kernel options for the interpreter.  Global variable names
498 	 * can be found in acglobal.h.
499 	 */
500 	AcpiGbl_EnableInterpreterSlack = TRUE;
501 
502 	aml_simulation_regload("region.ini");
503 	if (load_dsdt(argv[1]) == 0) {
504 		aml_simulation_regdump("region.dmp");
505 	}
506 
507 	return (0);
508 }
509