| // Copyright 2020 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <inttypes.h> |
| #include <lib/acpi_lite.h> |
| #include <lib/acpi_lite/numa.h> |
| #include <lib/acpi_lite/structures.h> |
| |
| #include "binary_reader.h" |
| #include "debug.h" |
| |
| namespace acpi_lite { |
| |
| zx_status_t EnumerateCpuNumaPairs( |
| const AcpiSratTable* const srat, |
| const fit::inline_function<void(const AcpiNumaDomain&, uint32_t)>& callback) { |
| // Initialise the domains. |
| static constexpr size_t kMaxNumaDomains = 10; |
| AcpiNumaDomain domains[kMaxNumaDomains]; |
| for (uint32_t i = 0; i < kMaxNumaDomains; i++) { |
| domains[i] = AcpiNumaDomain{ |
| .domain = i, |
| .memory = {}, |
| .memory_count = 0, |
| }; |
| } |
| |
| // First find all NUMA domains. |
| BinaryReader reader = BinaryReader::FromPayloadOfStruct(srat); |
| while (!reader.empty()) { |
| const AcpiSubTableHeader* sub_header = reader.Read<AcpiSubTableHeader>(); |
| if (sub_header == nullptr) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| // Ignore anything not ACPI_SRAT_TYPE_MEMORY_AFFINITY. |
| if (sub_header->type != ACPI_SRAT_TYPE_MEMORY_AFFINITY) { |
| continue; |
| } |
| |
| const auto* mem = Downcast<AcpiSratMemoryAffinityEntry>(sub_header); |
| if (mem == nullptr) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| // Ignore disabled entries. |
| if (!(mem->flags & ACPI_SRAT_FLAG_ENABLED)) { |
| continue; |
| } |
| |
| // Ensure proximity domain is valid. |
| if (mem->proximity_domain >= kMaxNumaDomains) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| // Ensure we haven't seen too many entries for this domain. |
| auto& domain = domains[mem->proximity_domain]; |
| if (domain.memory_count >= kAcpiMaxNumaRegions) { |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| uint64_t base = (static_cast<uint64_t>(mem->base_address_high) << 32) | mem->base_address_low; |
| uint64_t length = (static_cast<uint64_t>(mem->length_high) << 32) | mem->length_low; |
| domain.memory[domain.memory_count++] = {.base_address = base, .length = length}; |
| |
| LOG_DEBUG("acpi_lite: ACPI SRAT: numa Region:{ domain: %" PRIu32 " base: %#" PRIx64 |
| " length: %#" PRIx64 " (%" PRIu64 ") }\n", |
| mem->proximity_domain, base, length, length); |
| } |
| |
| // Then visit all CPU APIC IDs and provide the accompanying NUMA region. |
| reader = BinaryReader::FromPayloadOfStruct(srat); |
| while (!reader.empty()) { |
| const AcpiSubTableHeader* sub_header = reader.Read<AcpiSubTableHeader>(); |
| if (sub_header == nullptr) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| if (sub_header->type == ACPI_SRAT_TYPE_PROCESSOR_AFFINITY) { |
| const auto* cpu = Downcast<AcpiSratProcessorAffinityEntry>(sub_header); |
| if (cpu == nullptr) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| // Ignore disabled entries. |
| if (!(cpu->flags & ACPI_SRAT_FLAG_ENABLED)) { |
| continue; |
| } |
| |
| // Ensure the domain is in bounds. |
| uint32_t domain = cpu->proximity_domain(); |
| if (domain >= kMaxNumaDomains) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| callback(domains[domain], cpu->apic_id); |
| } else if (sub_header->type == ACPI_SRAT_TYPE_PROCESSOR_X2APIC_AFFINITY) { |
| const auto* cpu = Downcast<AcpiSratProcessorX2ApicAffinityEntry>(sub_header); |
| if (cpu == nullptr) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| // Ignore disabled entries. |
| if (!(cpu->flags & ACPI_SRAT_FLAG_ENABLED)) { |
| continue; |
| } |
| |
| // Ensure it is in bounds. |
| uint32_t domain = cpu->proximity_domain; |
| if (domain >= kMaxNumaDomains) { |
| return ZX_ERR_INTERNAL; |
| } |
| |
| callback(domains[cpu->proximity_domain], cpu->x2apic_id); |
| } |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t EnumerateCpuNumaPairs( |
| const AcpiParserInterface& parser, |
| fit::inline_function<void(const AcpiNumaDomain&, uint32_t)> callback) { |
| // Get the SRAT table. |
| const AcpiSratTable* srat = GetTableByType<AcpiSratTable>(parser); |
| if (srat == nullptr) { |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| return EnumerateCpuNumaPairs(srat, callback); |
| } |
| |
| } // namespace acpi_lite |