// // Created by rick on 22-08-21. // #include #include #include #include #include #include #include #include #include #include void acpi_handle_facp(const struct acpi_sdt_header *addr); void acpi_handle_apic(const struct acpi_sdt_header *addr); struct acpi_type acpi_types[] = { {"APIC", "Multiple APIC Description Table (MADT)", acpi_handle_apic}, {"BGRT", "Boot Graphics Resource Table (BGRT; only supported on UEFI systems)", NULL}, {"BERT", "Boot Error Record Table (BERT)", NULL}, {"CPEP", "Corrected Platform Error Polling Table (CPEP)", NULL}, {"DSDT", "Differentiated System Description Table (DSDT)", NULL}, {"ECDT", "Embedded Controller Boot Resources Table (ECDT)", NULL}, {"EINJ", "Error Injection Table (EINJ)", NULL}, {"ERST", "Error Record Serialization Table (ERST)", NULL}, {"FACP", "Fixed ACPI Description Table (FADT)", acpi_handle_facp}, {"FACS", "Firmware ACPPI Control Structure (FACS)", NULL}, {"HEST", "Hardware Error Source Table (HEST)", NULL}, {"MSCT", "Maximum System Characteristics Table (MSCT)", NULL}, {"MPST", "Memory Power State Table (MPST)", NULL}, // {"OEMx", "OEM Specific Information Tables (Any table with a signature beginning with \"OEM\" falls into this definition)", NULL}, {"PMTT", "latform Memory Topology Table (PMTT)", NULL}, {"PSDT", "Persistent System Description Table (PSDT)", NULL}, {"RASF", "ACPI RAS FeatureTable (RASF)", NULL}, {"RSDT", "Root System Description Table (RSDT; 32-bit version of the XSDT)", NULL}, {"SBST", "Smart Battery Specification Table (SBST)", NULL}, {"SLIT", "System Locality System Information Table (SLIT)", NULL}, {"SRAT", "System Resource Affinity Table (SRAT)", NULL}, {"SSDT", "Secondary System Description Table (SSDT)", NULL}, {"XSDT", "Extended System Description Table (This wiki page; included for completeness)", NULL}, {"\0\0\0\0", NULL, NULL}, }; bool acpi_checksum(const void *table_addr, const size_t table_size) { if (table_size < 1) { return false; // invalid input } const uint8_t *table = table_addr; uint8_t val = 0; for (size_t i = 0; i < table_size; ++i) { val += table[i]; } return val == 0; } const struct acpi_rsdt *rsdt_table = NULL; const struct acpi_xsdt *xsdt_table = NULL; const struct acpi_fadt_t *fadt; const struct acpi_sdt_header *dsdt; void acpi_handle_facp(const struct acpi_sdt_header *addr) { // parse the FACP/FADT fadt = (const struct acpi_fadt_t *) addr; if (fadt->header.revision > 5) { // minor since 5.1 printf("ACPI Revision: %i.%i %.6s\n", fadt->header.revision, fadt->minor_version, fadt->header.oem); } else { printf("ACPI Revision: %i.0 %.6s\n", fadt->header.revision, fadt->header.oem); } if (fadt->x_dsdt != 0 && fadt->x_dsdt < UINT32_MAX) { dsdt = (struct acpi_sdt_header *) fadt->x_dsdt; } else if (fadt->dsdt != 0) { dsdt = (struct acpi_sdt_header *) fadt->dsdt; } else { k_panics("No DSDT"); } if (!acpi_checksum(dsdt, dsdt->length)) { k_panics("DSDT is not valid"); } } void acpi_handle_apic(const struct acpi_sdt_header *addr) { const struct acpi_madt *madt = (const struct acpi_madt *) addr; printf("Local apic: %p %i\n", madt->local_apic_addr, madt->flags_int); lapic_set_addr(madt->local_apic_addr); const uint8_t *current = madt->entries; const uint8_t *end = (const uint8_t *) (madt) + madt->header.length; while (current < end) { struct acpi_madt_entry_header *current_header = (struct acpi_madt_entry_header *) current; switch (current_header->type) { case MADT_TYPE_PROCESSOR_LOCAL_APIC: { const struct acpi_madt_local_apic *la = (const struct acpi_madt_local_apic *) current_header; printf("Local APIC %x for %x %x\n", la->apic_id, la->processor_id, la->flags); break; } case MADT_TYPE_IO_APIC: { const struct acpi_madt_io_apic *ia = (const struct acpi_madt_io_apic *) current_header; printf("IO APIC %x at %x with %x\n", ia->apic_id, ia->io_apic_address, ia->global_system_interrupt_base); break; } case MADT_TYPE_IO_APIC_INTERRUPT_SOURCE_OVERRIDE: { const struct acpi_madt_io_apic_interrupt_source_override *override = (const struct acpi_madt_io_apic_interrupt_source_override *) current_header; printf("IO APIC Interrupt source override %x:%x -> %x %x\n", override->bus_source, override->irq_source, override->global_system_interrupt, override->flags); break; } case MADT_TYPE_IO_APIC_NON_MASKABLE_INTERRUPT_SOURCE: { const struct acpi_madt_io_apic_non_maskable_interrupt *nmi = (const struct acpi_madt_io_apic_non_maskable_interrupt *) current_header; printf("IO APIC NMI source %x %x %x\n", nmi->nmi_source, nmi->flags, nmi->global_system_interrupt); break; } case MADT_TYPE_LOCAL_APIC_NON_MASKABLE_INTERRUPTS: { const struct acpi_madt_local_apic_non_maskable_interrupts *nmi = (const struct acpi_madt_local_apic_non_maskable_interrupts *) current_header; printf("Local APIC NMI CPU %x %x %x\n", nmi->acpi_processor_id, nmi->flags, nmi->lint_no); break; } case MADT_TYPE_LOCAL_APIC_ADDRESS_OVERRIDE: { const struct acpi_madt_local_apic_address_override *ao = (const struct acpi_madt_local_apic_address_override *) current_header; printf("Local APIC addr override %x%x\n", ao->physical_address_local_apic >> 32, ao->physical_address_local_apic & UINT32_MAX); if (ao->physical_address_local_apic > UINT32_MAX) { printf("Override above 4GB!"); } else { lapic_set_addr(ao->physical_address_local_apic & UINT32_MAX); } break; } case MADT_TYPE_PROCESSOR_LOCAL_X2_APIC: { const struct acpi_madt_processor_local_x2_apic *x2 = (const struct acpi_madt_processor_local_x2_apic *) current_header; printf("Local x2APIC %x %x %x\n", x2->processors_local_x2_apic_id, x2->flags, x2->acpi_id); break; } default: printf("Unknown MADT entry %x", current_header->type); break; } current += current_header->length; } printf("Lapic id %x v%x\n", lapic_get_id(), lapic_get_version()); } void acpi_parse_table(uint32_t table_addr, uint32_t index) { struct acpi_sdt_header *s_header = (struct acpi_sdt_header *) table_addr; if (!acpi_checksum(s_header, s_header->length)) { printf("%i: invalid acpi entry\n", index); return; } struct acpi_type *c = &acpi_types[0]; while (c->name[0] != '\0') { if (strncmp(s_header->signature, c->name, 4) == 0) { printf("%i: %.4s %s\n", index, c->name, c->description); if (c->handle_table != NULL) { c->handle_table(s_header); } break; } c += 1; } if (c->name[0] == '\0') { printf("%i: %.4s unknown\n", index, s_header->signature); } } void acpi_parse_rsdt(uint32_t address) { printf("Parsing RSDT\n"); rsdt_table = (const struct acpi_rsdt *) address; if (!acpi_checksum(rsdt_table, rsdt_table->header.length)) { k_panics("Malformed RSDT"); } if (strncmp(rsdt_table->header.signature, "RSDT", 4) != 0) { k_panics("RSDT pointer is not RSDT"); } uint32_t entries = (rsdt_table->header.length - sizeof(struct acpi_sdt_header)) / sizeof(uint32_t); for (uint32_t i = 0; i < entries; ++i) { acpi_parse_table(rsdt_table->pointer_to_other_sdt[i], i); } } void acpi_parse_xsdt(uint64_t address) { printf("Parsing XSDT\n"); if (address > UINT32_MAX) { k_panics("acpi table above 4GB"); } xsdt_table = (const struct acpi_xsdt *) (uint32_t) address; if (!acpi_checksum(xsdt_table, xsdt_table->header.length)) { k_panics("Malformed XSDT"); } if (strncmp(xsdt_table->header.signature, "XSDT", 4) != 0) { k_panics("XSDT pointer is not XSDT"); } uint32_t entries = (xsdt_table->header.length - sizeof(struct acpi_sdt_header)) / sizeof(uint64_t); for (uint32_t i = 0; i < entries; ++i) { const uint64_t entry_addr = xsdt_table->pointer_to_other_sdt[i]; if (entry_addr > UINT32_MAX) { printf("%i: Above 4GB\n", i); continue; } acpi_parse_table(xsdt_table->pointer_to_other_sdt[i], i); } } void *acpi_find_table_by_name(const char *name, int skip) { if (xsdt_table != NULL) { uint32_t entries = (xsdt_table->header.length - sizeof(struct acpi_sdt_header)) / sizeof(uint64_t); for (uint32_t i = 0; i < entries; ++i) { const uint64_t entry_addr = xsdt_table->pointer_to_other_sdt[i]; if (entry_addr > UINT32_MAX) { printf("%i: Above 4GB\n", i); continue; } struct acpi_sdt_header *header = (struct acpi_sdt_header *) xsdt_table->pointer_to_other_sdt[i]; if (!acpi_checksum(header, header->length)) { printf("Invalid header %d", i); continue; } if (strncmp(header->signature, name, 4) == 0) { if (skip > 0) { skip--; continue; } return header; } } } if (rsdt_table != NULL) { uint32_t entries = (rsdt_table->header.length - sizeof(struct acpi_sdt_header)) / sizeof(uint32_t); for (uint32_t i = 0; i < entries; ++i) { struct acpi_sdt_header *header = (struct acpi_sdt_header *) rsdt_table->pointer_to_other_sdt[i]; if (!acpi_checksum(header, header->length)) { printf("Invalid header %d", i); continue; } if (strncmp(header->signature, name, 4) == 0) { if (skip > 0) { skip--; continue; } return header; } } } // check if DSDT is requested, but was not present as normal table if (strncmp(name, "DSDT", 4) == 0) { return (void *) dsdt; } return NULL; } void acpi_parse() { struct lai_rsdp_info lai_rsdp_info; lai_api_error_t result = lai_bios_detect_rsdp(&lai_rsdp_info); if (result != LAI_ERROR_NONE) { printf("LAI: could not find rsdp: %s\n", lai_api_error_to_string(result)); k_panics("Could not init ACPI"); } lai_set_acpi_revision(lai_rsdp_info.acpi_version); if (lai_rsdp_info.xsdt_address != 0) { acpi_parse_xsdt(lai_rsdp_info.xsdt_address); } else if (lai_rsdp_info.rsdt_address != 0) { acpi_parse_rsdt(lai_rsdp_info.rsdt_address); } else { k_panics("No rsdt nor xsdt"); } } void acpi_init() { acpi_parse(); lai_create_namespace(); } INIT_FUNCTION(100) = { .name = "acpi", .stage = INIT_STAGE_EARLY_BOOT_1, .init = acpi_init, };