blob: 9279a1eba7918e13cf0086b574b56f52a35a2d15 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/zbi-format/cpu.h>
#include <lib/zbi-format/internal/deprecated-cpu.h>
#include <lib/zbitl/items/cpu-topology.h>
#include <algorithm>
namespace zbitl {
fit::result<std::string_view, CpuTopologyTable> CpuTopologyTable::FromPayload(
uint32_t item_type, zbitl::ByteView payload) {
switch (item_type) {
case ZBI_TYPE_CPU_TOPOLOGY: {
if (payload.empty()) {
return fit::error("ZBI_TYPE_CPU_TOPOLOGY payload is empty");
}
if (payload.size_bytes() % sizeof(zbi_topology_node_t) != 0) {
return fit::error("ZBI_TYPE_CPU_TOPOLOGY payload not a multiple of entry size");
}
CpuTopologyTable result;
result.table_ = cpp20::span{
reinterpret_cast<const zbi_topology_node_t*>(payload.data()),
payload.size_bytes() / sizeof(zbi_topology_node_t),
};
return fit::ok(result);
}
case ZBI_TYPE_DEPRECATED_CPU_TOPOLOGY_V2: {
if (payload.empty()) {
return fit::error("ZBI_TYPE_DEPRECATED_CPU_TOPOLOGY_V2 payload is empty");
}
if (payload.size_bytes() % sizeof(zbi_topology_node_v2_t) != 0) {
return fit::error(
"ZBI_TYPE_DEPRECATED_CPU_TOPOLOGY_V2 payload not a multiple of entry size");
}
CpuTopologyTable result;
result.table_ = cpp20::span{
reinterpret_cast<const zbi_topology_node_v2_t*>(payload.data()),
payload.size_bytes() / sizeof(zbi_topology_node_v2_t),
};
return fit::ok(result);
}
case ZBI_TYPE_DEPRECATED_CPU_TOPOLOGY_V1:
if (payload.size_bytes() >= sizeof(zbi_cpu_config_t)) {
auto conf = reinterpret_cast<const zbi_cpu_config_t*>(payload.data());
const size_t conf_size =
sizeof(zbi_cpu_config_t) + (conf->cluster_count * sizeof(zbi_cpu_cluster_t));
if (payload.size_bytes() < conf_size) {
return fit::error("ZBI_TYPE_DEPRECATED_CPU_TOPOLOGY_V1 too small for cluster count");
}
CpuTopologyTable result;
result.table_ = conf;
return fit::ok(result);
}
return fit::error("ZBI_TYPE_DEPRECATED_CPU_TOPOLOGY_V1 too small for header");
default:
return fit::error("invalid ZBI item type for CpuTopologyTable");
}
}
// These functions are static in the private inner class rather than just being
// in an anonymous namespace local to this file just so that the public classes
// can have a friend declaration.
struct CpuTopologyTable::Dispatch {
// Set up with the modern table format, just use the input as is.
static size_t TableSize(cpp20::span<const zbi_topology_node_t> nodes) { return nodes.size(); }
static iterator TableBegin(cpp20::span<const zbi_topology_node_t> nodes) {
iterator result;
result.it_ = nodes.begin();
return result;
}
static iterator TableEnd(cpp20::span<const zbi_topology_node_t> nodes) {
iterator result;
result.it_ = nodes.end();
return result;
}
static size_t TableSize(cpp20::span<const zbi_topology_node_v2_t> nodes) { return nodes.size(); }
static iterator TableBegin(cpp20::span<const zbi_topology_node_v2_t> nodes) {
V2ConvertingIterator it;
it.v2_nodes_ = nodes;
it.idx_ = 0;
iterator result;
result.it_ = it;
return result;
}
static iterator TableEnd(cpp20::span<const zbi_topology_node_v2_t> nodes) {
V2ConvertingIterator it;
it.v2_nodes_ = nodes;
it.idx_ = nodes.size();
iterator result;
result.it_ = it;
return result;
}
// Set up with the old table format, convert on the fly.
static size_t TableSize(const zbi_cpu_config_t* config) {
size_t nodes = 0;
cpp20::span clusters(config->clusters, config->cluster_count);
for (const zbi_cpu_cluster_t& cluster : clusters) {
// There's a node for the cluster, then a node for each CPU.
nodes += 1 + cluster.cpu_count;
}
return nodes;
}
static iterator TableBegin(const zbi_cpu_config_t* config) {
V1ConvertingIterator it;
if (config->cluster_count > 0) {
it.clusters_ = cpp20::span(config->clusters, config->cluster_count);
it.logical_id_ = 0;
}
iterator result;
result.it_ = it;
return result;
}
static iterator TableEnd(const zbi_cpu_config_t* config) {
iterator result;
result.it_ = V1ConvertingIterator();
return result;
}
static void Advance(V1ConvertingIterator& it) {
ZX_ASSERT_MSG(it.logical_id_, "cannot increment default-constructed or end iterator");
++it.next_node_idx_;
if (!it.cpu_idx_) {
// This is at the node for a cluster. Advance to its first CPU.
it.cpu_idx_ = 0;
return;
}
const uint32_t cpu_count = it.clusters_[it.cluster_idx_].cpu_count;
if (const uint32_t cpu_idx = *it.cpu_idx_; cpu_idx < cpu_count) {
++*it.logical_id_;
++*it.cpu_idx_;
// If there are still CPUs to process, advance to the next one; else,
// fall through to advance to the next cluster.
if (cpu_idx < cpu_count - 1) {
return;
}
}
// Advance to the next cluster, unless we have reached the end.
it.cluster_node_idx_ = it.next_node_idx_;
it.cpu_idx_ = std::nullopt;
if (++it.cluster_idx_ == it.clusters_.size()) {
it.logical_id_ = std::nullopt;
}
}
static zbi_topology_node_t GetNode(const V1ConvertingIterator& it) {
ZX_ASSERT_MSG(it.logical_id_, "cannot dereference default-constructed or end iterator");
// First there's a node for the cluster itself.
if (!it.cpu_idx_) {
return zbi_topology_node_t{
// We don't have this data so it is a guess that little cores are
// first.
.entity =
{
.discriminant = ZBI_TOPOLOGY_ENTITY_CLUSTER,
.cluster = {.performance_class = it.cluster_idx_},
},
.parent_index = ZBI_TOPOLOGY_NO_PARENT,
};
}
// Then there's a node for each CPU.
return zbi_topology_node_t{
.entity =
{
.discriminant = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.processor =
{
.architecture_info =
{
.discriminant = ZBI_TOPOLOGY_ARCHITECTURE_INFO_ARM64,
.arm64 =
{
.cluster_1_id = it.cluster_idx_,
.cpu_id = *it.cpu_idx_,
.gic_id = *it.logical_id_,
},
},
.logical_ids = {*it.logical_id_},
.logical_id_count = 1,
},
},
.parent_index = static_cast<uint16_t>(it.cluster_node_idx_),
};
}
static zbi_topology_node_t GetNode(const V2ConvertingIterator& it) {
ZX_ASSERT_MSG(it.idx_, "cannot dereference default-constructed iterator");
const zbi_topology_node_v2_t& v2_node = it.v2_nodes_[*it.idx_];
zbi_topology_node_t node = {.parent_index = v2_node.parent_index};
zbi_topology_entity_t& entity = node.entity;
auto& v2_entity = v2_node.entity;
switch (v2_node.entity_type) {
case ZBI_TOPOLOGY_ENTITY_V2_PROCESSOR: {
entity.discriminant = ZBI_TOPOLOGY_ENTITY_PROCESSOR;
entity.processor = zbi_topology_processor_t{
.flags = v2_entity.processor.flags,
.logical_id_count = v2_entity.processor.logical_id_count,
};
zbi_topology_architecture_info_t& arch_info = entity.processor.architecture_info;
switch (v2_entity.processor.architecture) {
case ZBI_TOPOLOGY_ARCHITECTURE_V2_X64:
arch_info.discriminant = ZBI_TOPOLOGY_ARCHITECTURE_INFO_X64;
arch_info.x64 = v2_entity.processor.architecture_info.x64;
break;
case ZBI_TOPOLOGY_ARCHITECTURE_V2_ARM64:
arch_info.discriminant = ZBI_TOPOLOGY_ARCHITECTURE_INFO_ARM64;
arch_info.arm64 = v2_entity.processor.architecture_info.arm64;
break;
case ZBI_TOPOLOGY_ARCHITECTURE_V2_RISCV64:
arch_info.discriminant = ZBI_TOPOLOGY_ARCHITECTURE_INFO_RISCV64;
arch_info.riscv64 = v2_entity.processor.architecture_info.riscv64;
break;
}
memcpy(entity.processor.logical_ids, v2_entity.processor.logical_ids,
sizeof(entity.processor.logical_ids));
break;
}
case ZBI_TOPOLOGY_ENTITY_V2_CLUSTER:
entity.discriminant = ZBI_TOPOLOGY_ENTITY_CLUSTER;
entity.cluster = v2_entity.cluster;
break;
case ZBI_TOPOLOGY_ENTITY_V2_CACHE:
entity.discriminant = ZBI_TOPOLOGY_ENTITY_CACHE;
entity.cache = v2_entity.cache;
break;
case ZBI_TOPOLOGY_ENTITY_V2_NUMA_REGION:
entity.discriminant = ZBI_TOPOLOGY_ENTITY_NUMA_REGION;
entity.numa_region = zbi_topology_numa_region_t{
.start = v2_entity.numa_region.start_address,
.size = v2_entity.numa_region.end_address - v2_entity.numa_region.start_address,
};
break;
}
return node;
}
};
size_t CpuTopologyTable::size() const {
return std::visit([](const auto& table) { return Dispatch::TableSize(table); }, table_);
}
CpuTopologyTable::iterator CpuTopologyTable::begin() const {
return std::visit([](const auto& table) { return Dispatch::TableBegin(table); }, table_);
}
CpuTopologyTable::iterator CpuTopologyTable::end() const {
return std::visit([](const auto& table) { return Dispatch::TableEnd(table); }, table_);
}
CpuTopologyTable::V1ConvertingIterator& CpuTopologyTable::V1ConvertingIterator::operator++() {
Dispatch::Advance(*this);
return *this;
}
zbi_topology_node_t CpuTopologyTable::V1ConvertingIterator::operator*() const {
return Dispatch::GetNode(*this);
}
zbi_topology_node_t CpuTopologyTable::V2ConvertingIterator::operator*() const {
return Dispatch::GetNode(*this);
}
} // namespace zbitl