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/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 97 AcpiOsGetRootPointer(void) 98 { 99 100 return (0); 101 } 102 103 void 104 AeDoObjectOverrides(void) 105 { 106 } 107 108 void 109 AeTableOverride(ACPI_TABLE_HEADER *ExistingTable, ACPI_TABLE_HEADER **NewTable) 110 { 111 } 112 113 void 114 MpSaveGpioInfo(ACPI_PARSE_OBJECT *Op, AML_RESOURCE *Resource, 115 UINT32 PinCount, UINT16 *PinList, char *DeviceName) 116 { 117 } 118 119 void 120 MpSaveSerialInfo(ACPI_PARSE_OBJECT *Op, AML_RESOURCE *Resource, 121 char *DeviceName) 122 { 123 } 124 125 static 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 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 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 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 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 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 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 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 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 508 usage(const char *progname) 509 { 510 511 printf("usage: %s dsdt_file\n", progname); 512 exit(1); 513 } 514 515 int 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