blob: 82ab9a41c1588ee1db1c6c4f8e3ef0d5dcf6d142 [file] [log] [blame]
// 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