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