acpi_table_header_t *mon_acpi_locate_table(char *sig) { acpi_table_rsdp_t *rsdp = NULL; void *table = NULL; /* Try 0x0 first for getting rsdp table */ rsdp = scan_for_rsdp(acpi_map_memory(0), 0x400); if (NULL == rsdp) { /* Try 0xE0000 */ MON_LOG(mask_anonymous, level_trace, "Try 0xE0000 for ACPI RSDP table\n"); rsdp = scan_for_rsdp(acpi_map_memory(0xE0000), 0x1FFFF); } if (NULL == rsdp) { MON_LOG(mask_anonymous, level_error, "Could not find the rsdp table\n"); return NULL; } MON_LOG(mask_anonymous, level_trace, "rsdp address %p\n", rsdp); /* Get the specified table from rsdp */ table = get_acpi_table_from_rsdp(rsdp, sig); return table; }
static struct acpi_table_header *acpi_map_table(unsigned long where, char *sig) { unsigned size; struct acpi_table_header *tbl = (struct acpi_table_header *) acpi_map_memory(where, sizeof(struct acpi_table_header)); if (!tbl || (sig && memcmp(sig, tbl->signature, 4))) return 0; size = tbl->length; acpi_unmap_memory((u8 *) tbl, sizeof(struct acpi_table_header)); return (struct acpi_table_header *)acpi_map_memory(where, size); }
int main(int argc, char **argv) { int option_index, c, fd; u8 *raw; struct acpi_rsdp_descriptor rsdpx, *x = 0; char *filename = 0; char buff[80]; memset(select_sig, 0, 4); print = 1; connect = 0; addr = length = 0; skip = 0; while (1) { option_index = 0; c = getopt_long(argc, argv, "a:t:o:bl:s:h", long_options, &option_index); if (c == -1) break; switch (c) { case 0: switch (option_index) { case 0: addr = strtoul(optarg, (char **)NULL, 16); break; case 1: memcpy(select_sig, optarg, 4); break; case 2: filename = optarg; break; case 3: print = 0; break; case 4: length = strtoul(optarg, (char **)NULL, 16); break; case 5: skip = strtoul(optarg, (char **)NULL, 10); break; case 6: usage(argv[0]); exit(0); } break; case 'a': addr = strtoul(optarg, (char **)NULL, 16); break; case 't': memcpy(select_sig, optarg, 4); break; case 'o': filename = optarg; break; case 'b': print = 0; break; case 'l': length = strtoul(optarg, (char **)NULL, 16); break; case 's': skip = strtoul(optarg, (char **)NULL, 10); break; case 'h': usage(argv[0]); exit(0); default: printf("Unknown option!\n"); usage(argv[0]); exit(0); } } fd = STDOUT_FILENO; if (filename) { fd = creat(filename, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); if (fd < 0) return fd; } if (!select_sig[0] && !print) { connect = 1; } psz = sysconf(_SC_PAGESIZE); if (length && addr) { /* We know length and address, it means we just want a memory dump */ if (!(raw = acpi_map_memory(addr, length))) goto not_found; write(fd, raw, length); acpi_unmap_memory(raw, length); close(fd); return 0; } length = sizeof(struct acpi_rsdp_descriptor); if (!addr) { addr = read_efi_systab(); if (!addr) { addr = ACPI_HI_RSDP_WINDOW_BASE; length = ACPI_HI_RSDP_WINDOW_SIZE; } } if (!(raw = acpi_map_memory(addr, length)) || !(x = acpi_scan_for_rsdp(raw, length))) goto not_found; /* Find RSDP and print all found tables */ memcpy(&rsdpx, x, sizeof(struct acpi_rsdp_descriptor)); acpi_unmap_memory(raw, length); if (connect) { lseek(fd, sizeof(struct acpi_rsdp_descriptor), SEEK_SET); } if (!acpi_dump_SDT(fd, &rsdpx)) goto not_found; if (connect) { lseek(fd, 0, SEEK_SET); write(fd, x, (rsdpx.revision < 2) ? ACPI_RSDP_CHECKSUM_LENGTH : ACPI_RSDP_XCHECKSUM_LENGTH); } else if (!select_sig[0] || !memcmp("RSD PTR ", select_sig, 4)) { addr += (long)x - (long)raw; length = snprintf(buff, 80, "RSD PTR @ %p\n", (void *)addr); write(fd, buff, length); acpi_show_data(fd, (u8 *) & rsdpx, (rsdpx.revision < 2) ? ACPI_RSDP_CHECKSUM_LENGTH : ACPI_RSDP_XCHECKSUM_LENGTH); buff[0] = '\n'; write(fd, buff, 1); } acpi_dump_dynamic_SSDT(fd); close(fd); return 0; not_found: close(fd); fprintf(stderr, "ACPI tables were not found. If you know location " "of RSD PTR table (from dmesg, etc), " "supply it with either --addr or -a option\n"); return 1; }
/* * SLP_TYP values are programmed in PM1A and PM1B control block registers * to initiate power transition. Each Sx state has a corresponding SLP_TYP * value. SLP_TYP values are stored in DSDT area of ACPI tables as AML packages * Following code searches for these packages to retreive the SLP_TYPs * * Search for '_SX_' to get to start of package. 'X' stands for sleep state * e.g. '_S3_'. If '_SX_' is not found then it means the system does not support * that sleep state. * * _SX_packages are in the following format * * 1 byte : Package Op (0x12) * * 1 byte * + 'Package length' size : 'Package length' field. Refer ACPI spec for * 'Package length Encoding' High 2 bits of first * byte indicates how many bytes are used by * 'Package length' * If 0, then only the first byte is used * If > 0 then following bytes (max 3) will be also * used * * 1 byte : 'Number of Elements' * * 1 byte optional : There may be an optional 'Byte Prefix' (0x0A) * present. * * 1 byte SLP_TYP_A : SLP_TYP value for PM1A control block * * 1 byte optional : There may be an optional 'Byte Prefix' (0x0A) * present. * * 1 byte SLP_TYP_B : SLP_TYP value for PM1B control block * * Remaining bytes are ignored. */ void mon_acpi_retrieve_sleep_states(void) { acpi_table_header_t *dsdt; char *aml_ptr; uint8_t sstate; uint32_t i; dsdt = acpi_map_memory((uint64_t)fadt.dsdt); if (!dsdt) { MON_LOG(mask_anonymous, level_error, "[ACPI] DSDT not found\n"); return; } MON_LOG(mask_anonymous, level_trace, "SleepState | SleepTypeA | SleepTypeB\n"); MON_LOG(mask_anonymous, level_trace, "------------------------------------\n"); for (sstate = ACPI_STATE_S0; sstate < ACPI_S_STATE_COUNT; ++sstate) { aml_ptr = (char *)(dsdt + sizeof(acpi_table_header_t)); sleep_conversion_table[ACPI_PM1_CNTRL_REG_A][sstate] = 0xff; sleep_conversion_table[ACPI_PM1_CNTRL_REG_B][sstate] = 0xff; /* Search for '_SX_' string where 'X' is the sleep state e.g. '_S3_' */ for (i = 0; i < dsdt->length - 8; i++) { if (aml_ptr[0] == '_' && aml_ptr[1] == 'S' && aml_ptr[2] == ('0' + sstate) && aml_ptr[3] == '_') { break; } aml_ptr++; } if (i < dsdt->length - 8) { /* Skip '_SX_' and Package Op */ aml_ptr += 5; /* Skip 'Package length' bytes indicated by the 2 high bits of * 'Package Lead' byte */ aml_ptr += (*aml_ptr >> 6); /* Skip 'Package Lead' byte */ aml_ptr++; /* Skip 'Number of Elements' byte */ aml_ptr++; /* Skip 'Byte Prefix' if found */ if (*aml_ptr == 0x0a) { aml_ptr++; } /* This should be SLP_TYP value for PM1A_CNT_BLK */ sleep_conversion_table[ACPI_PM1_CNTRL_REG_A][sstate] = *aml_ptr; aml_ptr++; /* Skip 'Byte Prefix' if found */ if (*aml_ptr == 0x0a) { aml_ptr++; } /* This should be SLP_TYP value for PM1B_CNT_BLK */ sleep_conversion_table[ACPI_PM1_CNTRL_REG_B][sstate] = *aml_ptr; } MON_LOG(mask_anonymous, level_trace, " %3d | %3d | %3d\n", sstate, sleep_conversion_table[ACPI_PM1_CNTRL_REG_A][sstate], sleep_conversion_table[ACPI_PM1_CNTRL_REG_B][sstate]); }
/* Find an acpi table with specified signature and return mapped address */ INLINE acpi_table_header_t *get_acpi_table_from_rsdp(acpi_table_rsdp_t *rsdp, char *sig) { acpi_table_header_t *sdt = NULL; acpi_table_header_t *tbl = NULL; int xsdt = 1; int i; int num; char *offset; /* Get xsdt pointer */ if (rsdp->revision > 1 && rsdp->xsdt_physical_address) { MON_LOG(mask_anonymous, level_trace, "rsdp->xsdt_physical_address %lx\n", rsdp->xsdt_physical_address); sdt = acpi_map_memory(rsdp->xsdt_physical_address); } /* Or get rsdt */ if (!sdt && rsdp->rsdt_physical_address) { xsdt = 0; MON_LOG(mask_anonymous, level_trace, "rsdp->rsdt_physical_address = %x\n", rsdp->rsdt_physical_address); sdt = acpi_map_memory(rsdp->rsdt_physical_address); } /* Check if the rsdt/xsdt table pointer is NULL */ if (NULL == sdt) { MON_LOG(mask_anonymous, level_error, "map rsdt/xsdt error\n"); return NULL; } /* Make sure the table checksum is correct */ if (checksum((unsigned char *)sdt, sdt->length)) { MON_LOG(mask_anonymous, level_error, "Wrong checksum in %s!\n", (xsdt) ? "XSDT" : "RSDT"); return NULL; } MON_LOG(mask_anonymous, level_trace, "xsdt/rsdt checksum verified!\n"); /* Calculate the number of table pointers in the xsdt or rsdt table */ num = (sdt->length - sizeof(acpi_table_header_t)) / ((xsdt) ? sizeof(uint64_t) : sizeof(uint32_t)); MON_LOG(mask_anonymous, level_trace, "The number of table pointers in xsdt/rsdt = %d\n", num); /* Get to the table pointer area */ offset = (char *)sdt + sizeof(acpi_table_header_t); /* Traverse the pointer list to get the desired acpi table */ for (i = 0; i < num; ++i, offset += ((xsdt) ? sizeof(uint64_t) : sizeof(uint32_t))) { /* Get the address from the pointer entry */ tbl = acpi_map_memory((uint64_t) ((xsdt) ? (*(uint64_t *)offset) : (*(uint32_t *)offset))); /* Make sure address is valid */ if (!tbl) { continue; } MON_LOG(mask_anonymous, level_trace, "Mapped ACPI table addr = %p, ", tbl); MON_LOG(mask_anonymous, level_trace, "signature = %c%c%c%c\n", tbl->signature[0], tbl->signature[1], tbl->signature[2], tbl->signature[3]); /* Verify table signature & table checksum */ if ((0 == mon_memcmp(tbl->signature, sig, 4)) && !checksum((unsigned char *)tbl, tbl->length)) { /* Found the table with matched signature */ MON_LOG(mask_anonymous, level_trace, "Found the table %s address = %p length = %x\n", sig, tbl, tbl->length); return tbl; } } MON_LOG(mask_anonymous, level_error, "Could not find %s table in XSDT/RSDT\n", sig); return NULL; }