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