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