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