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 #include <contrib/dev/acpica/include/amlresrc.h> 49 50 /* 51 * Dummy DSDT Table Header 52 */ 53 54 static 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 62 static int aml_debug_prompt = 1; 63 64 struct ACPIRegionContent { 65 TAILQ_ENTRY(ACPIRegionContent) links; 66 int regtype; 67 ACPI_PHYSICAL_ADDRESS addr; 68 UINT8 value; 69 }; 70 71 TAILQ_HEAD(ACPIRegionContentList, ACPIRegionContent); 72 static struct ACPIRegionContentList RegionContentList; 73 74 static int aml_simulation_initialized = 0; 75 76 ACPI_PHYSICAL_ADDRESS AeLocalGetRootPointer(void); 77 void AeDoObjectOverrides(void); 78 void AeTableOverride(ACPI_TABLE_HEADER *, ACPI_TABLE_HEADER **); 79 80 static void aml_simulation_init(void); 81 static int aml_simulate_regcontent_add(int regtype, 82 ACPI_PHYSICAL_ADDRESS addr, 83 UINT8 value); 84 static int aml_simulate_regcontent_read(int regtype, 85 ACPI_PHYSICAL_ADDRESS addr, 86 UINT8 *valuep); 87 static int aml_simulate_regcontent_write(int regtype, 88 ACPI_PHYSICAL_ADDRESS addr, 89 UINT8 *valuep); 90 static UINT64 aml_simulate_prompt(char *msg, UINT64 def_val); 91 static void aml_simulation_regload(const char *dumpfile); 92 static void aml_simulation_regdump(const char *dumpfile); 93 94 /* Stubs to simplify linkage to the ACPICA core subsystem. */ 95 ACPI_PHYSICAL_ADDRESS 96 AcpiOsGetRootPointer(void) 97 { 98 99 return (0); 100 } 101 102 void 103 AeDoObjectOverrides(void) 104 { 105 } 106 107 void 108 AeTableOverride(ACPI_TABLE_HEADER *ExistingTable, ACPI_TABLE_HEADER **NewTable) 109 { 110 } 111 112 void 113 MpSaveGpioInfo(ACPI_PARSE_OBJECT *Op, AML_RESOURCE *Resource, 114 UINT32 PinCount, UINT16 *PinList, char *DeviceName) 115 { 116 } 117 118 void 119 MpSaveSerialInfo(ACPI_PARSE_OBJECT *Op, AML_RESOURCE *Resource, 120 char *DeviceName) 121 { 122 } 123 124 static void 125 aml_simulation_init(void) 126 { 127 128 aml_simulation_initialized = 1; 129 TAILQ_INIT(&RegionContentList); 130 aml_simulation_regload("region.ini"); 131 } 132 133 static int 134 aml_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 150 static int 151 aml_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 169 static int 170 aml_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 187 static UINT64 188 aml_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 220 static void 221 aml_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 262 static void 263 aml_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 291 static ACPI_STATUS 292 aml_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); \ 357 static ACPI_STATUS \ 358 aml_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 368 DECLARE_VM_SPACE_HANDLER(system_memory, ACPI_ADR_SPACE_SYSTEM_MEMORY); 369 DECLARE_VM_SPACE_HANDLER(system_io, ACPI_ADR_SPACE_SYSTEM_IO); 370 DECLARE_VM_SPACE_HANDLER(pci_config, ACPI_ADR_SPACE_PCI_CONFIG); 371 DECLARE_VM_SPACE_HANDLER(ec, ACPI_ADR_SPACE_EC); 372 DECLARE_VM_SPACE_HANDLER(smbus, ACPI_ADR_SPACE_SMBUS); 373 DECLARE_VM_SPACE_HANDLER(cmos, ACPI_ADR_SPACE_CMOS); 374 DECLARE_VM_SPACE_HANDLER(pci_bar_target,ACPI_ADR_SPACE_PCI_BAR_TARGET); 375 376 /* 377 * Load DSDT data file and invoke debugger 378 */ 379 380 static int 381 load_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 AcpiInitializeDebugger(); 484 AcpiGbl_DebuggerConfiguration = 0; 485 AcpiDbUserCommands(':', NULL); 486 487 if (strcmp(dsdtfile, filetmp) != 0) { 488 unlink(filetmp); 489 } 490 491 return (0); 492 } 493 494 static void 495 usage(const char *progname) 496 { 497 498 printf("usage: %s dsdt_file\n", progname); 499 exit(1); 500 } 501 502 int 503 main(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