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