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