blob: fc8e07fef6a9c1d145e90a5c7d0b9304b0c05853 [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/zbitl/items/cpu-topology.h>
#include <algorithm>
namespace zbitl {
fitx::result<std::string_view, CpuTopologyTable> CpuTopologyTable::FromPayload(
uint32_t item_type, zbitl::ByteView payload) {
switch (item_type) {
case ZBI_TYPE_CPU_TOPOLOGY:
if (payload.size_bytes() == 0) {
return fitx::error("ZBI_TYPE_CPU_TOPOLOGY payload is empty");
}
if (payload.size_bytes() % sizeof(zbi_topology_node_t) == 0) {
CpuTopologyTable result;
result.table_ = cpp20::span{
reinterpret_cast<const zbi_topology_node_t*>(payload.data()),
payload.size_bytes() / sizeof(zbi_topology_node_t),
};
return fitx::ok(result);
}
return fitx::error("ZBI_TYPE_CPU_TOPOLOGY payload not a multiple of entry size");
case ZBI_TYPE_CPU_CONFIG:
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 fitx::error("ZBI_TYPE_CPU_CONFIG too small for cluster count");
}
CpuTopologyTable result;
result.table_ = conf;
return fitx::ok(result);
}
return fitx::error("ZBI_TYPE_CPU_CONFIG too small for header");
default:
return fitx::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;
}
// 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) {
ConvertingIterator 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_ = ConvertingIterator();
return result;
}
static void Advance(ConvertingIterator& 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 ConvertingIterator& 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{
.entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER,
.parent_index = ZBI_TOPOLOGY_NO_PARENT,
// We don't have this data so it is a guess that little cores are
// first.
.entity = {.cluster = {.performance_class = it.cluster_idx_}},
};
}
// Then there's a node for each CPU.
return zbi_topology_node_t{
.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
.parent_index = static_cast<uint16_t>(it.cluster_node_idx_),
.entity =
{
.processor =
{
.logical_ids = {*it.logical_id_},
.logical_id_count = 1,
.architecture = ZBI_TOPOLOGY_ARCH_ARM,
.architecture_info =
{
.arm =
{
.cluster_1_id = it.cluster_idx_,
.cpu_id = *it.cpu_idx_,
.gic_id = *it.logical_id_,
},
},
},
},
};
}
};
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::ConvertingIterator& CpuTopologyTable::ConvertingIterator::operator++() {
Dispatch::Advance(*this);
return *this;
}
zbi_topology_node_t CpuTopologyTable::ConvertingIterator::operator*() const {
return Dispatch::GetNode(*this);
}
} // namespace zbitl