acpidb.c revision 284460
1217172Sjilles/*-
2217172Sjilles * Copyright (c) 2000-2002 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
3217172Sjilles * All rights reserved.
4217172Sjilles *
5217172Sjilles * Redistribution and use in source and binary forms, with or without
6217172Sjilles * modification, are permitted provided that the following conditions
7217172Sjilles * 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: stable/10/usr.sbin/acpi/acpidb/acpidb.c 284460 2015-06-16 20:00:53Z jkim $
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#include <contrib/dev/acpica/include/amlresrc.h>
49
50/*
51 * Dummy DSDT Table Header
52 */
53
54static ACPI_TABLE_HEADER dummy_dsdt_table = {
55	"DSDT", 123, 1, 123, "OEMID", "OEMTBLID", 1, "CRID", 1
56};
57
58/*
59 * Region space I/O routines on virtual machine
60 */
61
62static int	aml_debug_prompt = 1;
63
64struct ACPIRegionContent {
65	TAILQ_ENTRY(ACPIRegionContent) links;
66	int			regtype;
67	ACPI_PHYSICAL_ADDRESS	addr;
68	UINT8			value;
69};
70
71TAILQ_HEAD(ACPIRegionContentList, ACPIRegionContent);
72static struct	ACPIRegionContentList RegionContentList;
73
74static int		 aml_simulation_initialized = 0;
75
76ACPI_PHYSICAL_ADDRESS	 AeLocalGetRootPointer(void);
77void			 AeDoObjectOverrides(void);
78void			 AeTableOverride(ACPI_TABLE_HEADER *, ACPI_TABLE_HEADER **);
79
80static void		 aml_simulation_init(void);
81static int		 aml_simulate_regcontent_add(int regtype,
82			     ACPI_PHYSICAL_ADDRESS addr,
83			     UINT8 value);
84static int		 aml_simulate_regcontent_read(int regtype,
85			     ACPI_PHYSICAL_ADDRESS addr,
86			     UINT8 *valuep);
87static int		 aml_simulate_regcontent_write(int regtype,
88			     ACPI_PHYSICAL_ADDRESS addr,
89			     UINT8 *valuep);
90static UINT64		 aml_simulate_prompt(char *msg, UINT64 def_val);
91static void		 aml_simulation_regload(const char *dumpfile);
92static void		 aml_simulation_regdump(const char *dumpfile);
93
94/* Stubs to simplify linkage to the ACPICA core subsystem. */
95ACPI_PHYSICAL_ADDRESS
96AcpiOsGetRootPointer(void)
97{
98
99	return (0);
100}
101
102void
103AeDoObjectOverrides(void)
104{
105}
106
107void
108AeTableOverride(ACPI_TABLE_HEADER *ExistingTable, ACPI_TABLE_HEADER **NewTable)
109{
110}
111
112void
113MpSaveGpioInfo(ACPI_PARSE_OBJECT *Op, AML_RESOURCE *Resource,
114    UINT32 PinCount, UINT16 *PinList, char *DeviceName)
115{
116}
117
118void
119MpSaveSerialInfo(ACPI_PARSE_OBJECT *Op, AML_RESOURCE *Resource,
120    char *DeviceName)
121{
122}
123
124static void
125aml_simulation_init(void)
126{
127
128	aml_simulation_initialized = 1;
129	TAILQ_INIT(&RegionContentList);
130	aml_simulation_regload("region.ini");
131}
132
133static int
134aml_simulate_regcontent_add(int regtype, ACPI_PHYSICAL_ADDRESS addr, UINT8 value)
135{
136	struct	ACPIRegionContent *rc;
137
138	rc = malloc(sizeof(struct ACPIRegionContent));
139	if (rc == NULL) {
140		return (-1);	/* malloc fail */
141	}
142	rc->regtype = regtype;
143	rc->addr = addr;
144	rc->value = value;
145
146	TAILQ_INSERT_TAIL(&RegionContentList, rc, links);
147	return (0);
148}
149
150static int
151aml_simulate_regcontent_read(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			*valuep = rc->value;
161			return (1);	/* found */
162		}
163	}
164
165	*valuep = 0;
166	return (aml_simulate_regcontent_add(regtype, addr, *valuep));
167}
168
169static int
170aml_simulate_regcontent_write(int regtype, ACPI_PHYSICAL_ADDRESS addr, UINT8 *valuep)
171{
172	struct	ACPIRegionContent *rc;
173
174	if (!aml_simulation_initialized) {
175		aml_simulation_init();
176	}
177	TAILQ_FOREACH(rc, &RegionContentList, links) {
178		if (rc->regtype == regtype && rc->addr == addr) {
179			rc->value = *valuep;
180			return (1);	/* exists */
181		}
182	}
183
184	return (aml_simulate_regcontent_add(regtype, addr, *valuep));
185}
186
187static UINT64
188aml_simulate_prompt(char *msg, UINT64 def_val)
189{
190	char		buf[16], *ep;
191	UINT64		val;
192
193	val = def_val;
194	printf("DEBUG");
195	if (msg != NULL) {
196		printf("%s", msg);
197	}
198	printf("(default: 0x%jx ", (uintmax_t)val);
199	printf(" / %ju) >>", (uintmax_t)val);
200	fflush(stdout);
201
202	bzero(buf, sizeof buf);
203	while (1) {
204		if (read(0, buf, sizeof buf) == 0) {
205			continue;
206		}
207		if (buf[0] == '\n') {
208			break;	/* use default value */
209		}
210		if (buf[0] == '0' && buf[1] == 'x') {
211			val = strtoq(buf, &ep, 16);
212		} else {
213			val = strtoq(buf, &ep, 10);
214		}
215		break;
216	}
217	return (val);
218}
219
220static void
221aml_simulation_regload(const char *dumpfile)
222{
223	char	buf[256], *np, *ep;
224	struct	ACPIRegionContent rc;
225	FILE	*fp;
226
227	if (!aml_simulation_initialized) {
228		return;
229	}
230
231	if ((fp = fopen(dumpfile, "r")) == NULL) {
232		return;
233	}
234
235	while (fgets(buf, sizeof buf, fp) != NULL) {
236		np = buf;
237		/* reading region type */
238		rc.regtype = strtoq(np, &ep, 10);
239		if (np == ep) {
240			continue;
241		}
242		np = ep;
243
244		/* reading address */
245		rc.addr = strtoq(np, &ep, 16);
246		if (np == ep) {
247			continue;
248		}
249		np = ep;
250
251		/* reading value */
252		rc.value = strtoq(np, &ep, 16);
253		if (np == ep) {
254			continue;
255		}
256		aml_simulate_regcontent_write(rc.regtype, rc.addr, &rc.value);
257	}
258
259	fclose(fp);
260}
261
262static void
263aml_simulation_regdump(const char *dumpfile)
264{
265	struct	ACPIRegionContent *rc;
266	FILE	*fp;
267
268	if (!aml_simulation_initialized) {
269		return;
270	}
271	if ((fp = fopen(dumpfile, "w")) == NULL) {
272		warn("%s", dumpfile);
273		return;
274	}
275	while (!TAILQ_EMPTY(&RegionContentList)) {
276		rc = TAILQ_FIRST(&RegionContentList);
277		fprintf(fp, "%d	0x%jx	0x%x\n",
278		    rc->regtype, (uintmax_t)rc->addr, rc->value);
279		TAILQ_REMOVE(&RegionContentList, rc, links);
280		free(rc);
281	}
282
283	fclose(fp);
284	TAILQ_INIT(&RegionContentList);
285}
286
287/*
288 * Space handlers on virtual machine
289 */
290
291static ACPI_STATUS
292aml_vm_space_handler(
293	UINT32			SpaceID,
294	UINT32			Function,
295	ACPI_PHYSICAL_ADDRESS	Address,
296	UINT32			BitWidth,
297	UINT64			*Value,
298	int			Prompt)
299{
300	int			state;
301	UINT8			val;
302	UINT64			value, i;
303	char			msg[256];
304	static const char	*space_names[] = {
305		"SYSTEM_MEMORY", "SYSTEM_IO", "PCI_CONFIG",
306		"EC", "SMBUS", "CMOS", "PCI_BAR_TARGET"};
307
308	switch (Function) {
309	case ACPI_READ:
310		value = 0;
311		for (i = 0; (i * 8) < BitWidth; i++) {
312			state = aml_simulate_regcontent_read(SpaceID,
313							     Address + i, &val);
314			if (state == -1) {
315				return (AE_NO_MEMORY);
316			}
317			value |= val << (i * 8);
318		}
319		*Value = value;
320		if (Prompt) {
321			sprintf(msg, "[read (%s, %2d, 0x%jx)]",
322				space_names[SpaceID], BitWidth,
323				(uintmax_t)Address);
324			*Value = aml_simulate_prompt(msg, value);
325			if (*Value != value) {
326				return(aml_vm_space_handler(SpaceID,
327						ACPI_WRITE,
328						Address, BitWidth, Value, 0));
329			}
330		}
331		break;
332
333	case ACPI_WRITE:
334		value = *Value;
335		if (Prompt) {
336			sprintf(msg, "[write(%s, %2d, 0x%jx)]",
337				space_names[SpaceID], BitWidth,
338				(uintmax_t)Address);
339			value = aml_simulate_prompt(msg, *Value);
340		}
341		*Value = value;
342		for (i = 0; (i * 8) < BitWidth; i++) {
343			val = value & 0xff;
344			state = aml_simulate_regcontent_write(SpaceID,
345							      Address + i, &val);
346			if (state == -1) {
347				return (AE_NO_MEMORY);
348			}
349			value = value >> 8;
350		}
351	}
352
353	return (AE_OK);
354}
355
356#define DECLARE_VM_SPACE_HANDLER(name, id);			\
357static ACPI_STATUS						\
358aml_vm_space_handler_##name (					\
359	UINT32			Function,			\
360	ACPI_PHYSICAL_ADDRESS	Address,			\
361	UINT32			BitWidth,			\
362	UINT64			*Value)				\
363{								\
364	return (aml_vm_space_handler(id, Function, Address,	\
365		BitWidth, Value, aml_debug_prompt));		\
366}
367
368DECLARE_VM_SPACE_HANDLER(system_memory,	ACPI_ADR_SPACE_SYSTEM_MEMORY);
369DECLARE_VM_SPACE_HANDLER(system_io,	ACPI_ADR_SPACE_SYSTEM_IO);
370DECLARE_VM_SPACE_HANDLER(pci_config,	ACPI_ADR_SPACE_PCI_CONFIG);
371DECLARE_VM_SPACE_HANDLER(ec,		ACPI_ADR_SPACE_EC);
372DECLARE_VM_SPACE_HANDLER(smbus,		ACPI_ADR_SPACE_SMBUS);
373DECLARE_VM_SPACE_HANDLER(cmos,		ACPI_ADR_SPACE_CMOS);
374DECLARE_VM_SPACE_HANDLER(pci_bar_target,ACPI_ADR_SPACE_PCI_BAR_TARGET);
375
376/*
377 * Load DSDT data file and invoke debugger
378 */
379
380static int
381load_dsdt(const char *dsdtfile)
382{
383	char			filetmp[PATH_MAX];
384	u_int8_t		*code;
385	struct stat		sb;
386	int			fd, fd2;
387	int			error;
388
389	fd = open(dsdtfile, O_RDONLY, 0);
390	if (fd == -1) {
391		perror("open");
392		return (-1);
393	}
394	if (fstat(fd, &sb) == -1) {
395		perror("fstat");
396		close(fd);
397		return (-1);
398	}
399	code = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, (off_t)0);
400	if (code == NULL) {
401		perror("mmap");
402		return (-1);
403	}
404	if ((error = AcpiInitializeSubsystem()) != AE_OK) {
405		return (-1);
406	}
407
408	/*
409	 * make sure DSDT data contains table header or not.
410	 */
411	if (strncmp((char *)code, "DSDT", 4) == 0) {
412		strncpy(filetmp, dsdtfile, sizeof(filetmp));
413	} else {
414		mode_t	mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
415		dummy_dsdt_table.Length = sizeof(ACPI_TABLE_HEADER) + sb.st_size;
416		snprintf(filetmp, sizeof(filetmp), "%s.tmp", dsdtfile);
417		fd2 = open(filetmp, O_WRONLY | O_CREAT | O_TRUNC, mode);
418		if (fd2 == -1) {
419			perror("open");
420			return (-1);
421		}
422		write(fd2, &dummy_dsdt_table, sizeof(ACPI_TABLE_HEADER));
423
424		write(fd2, code, sb.st_size);
425		close(fd2);
426	}
427
428	/*
429	 * Install the virtual machine version of address space handlers.
430	 */
431	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
432			ACPI_ADR_SPACE_SYSTEM_MEMORY,
433			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_system_memory,
434			NULL, NULL)) != AE_OK) {
435		fprintf(stderr, "could not initialise SystemMemory handler: %d\n", error);
436		return (-1);
437	}
438	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
439			ACPI_ADR_SPACE_SYSTEM_IO,
440			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_system_io,
441			NULL, NULL)) != AE_OK) {
442		fprintf(stderr, "could not initialise SystemIO handler: %d\n", error);
443		return (-1);
444	}
445	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
446			ACPI_ADR_SPACE_PCI_CONFIG,
447			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_pci_config,
448			NULL, NULL)) != AE_OK) {
449		fprintf(stderr, "could not initialise PciConfig handler: %d\n", error);
450		return (-1);
451	}
452	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
453			ACPI_ADR_SPACE_EC,
454			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_ec,
455			NULL, NULL)) != AE_OK) {
456		fprintf(stderr, "could not initialise EC handler: %d\n", error);
457		return (-1);
458	}
459	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
460			ACPI_ADR_SPACE_SMBUS,
461			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_smbus,
462			NULL, NULL)) != AE_OK) {
463		fprintf(stderr, "could not initialise SMBUS handler: %d\n", error);
464		return (-1);
465	}
466	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
467			ACPI_ADR_SPACE_CMOS,
468			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_cmos,
469			NULL, NULL)) != AE_OK) {
470		fprintf(stderr, "could not initialise CMOS handler: %d\n", error);
471		return (-1);
472	}
473	if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
474			ACPI_ADR_SPACE_PCI_BAR_TARGET,
475			(ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_pci_bar_target,
476			NULL, NULL)) != AE_OK) {
477		fprintf(stderr, "could not initialise PCI BAR TARGET handler: %d\n", error);
478		return (-1);
479	}
480
481	AcpiDbGetTableFromFile(filetmp, NULL, TRUE);
482
483	AcpiDbInitialize();
484	AcpiGbl_DebuggerConfiguration = 0;
485	AcpiDbUserCommands(':', NULL);
486
487	if (strcmp(dsdtfile, filetmp) != 0) {
488		unlink(filetmp);
489	}
490
491	return (0);
492}
493
494static void
495usage(const char *progname)
496{
497
498	printf("usage: %s dsdt_file\n", progname);
499	exit(1);
500}
501
502int
503main(int argc, char *argv[])
504{
505	char	*progname;
506
507	progname = argv[0];
508
509	if (argc == 1) {
510		usage(progname);
511	}
512
513	AcpiDbgLevel = ACPI_DEBUG_DEFAULT;
514
515	/*
516	 * Match kernel options for the interpreter.  Global variable names
517	 * can be found in acglobal.h.
518	 */
519	AcpiGbl_EnableInterpreterSlack = TRUE;
520
521	aml_simulation_regload("region.ini");
522	if (load_dsdt(argv[1]) == 0) {
523		aml_simulation_regdump("region.dmp");
524	}
525
526	return (0);
527}
528