From f9ab594f1982d5f9e22de44ba61c5f57e938c14e Mon Sep 17 00:00:00 2001
From: Ed Coyne <edcoyne@google.com>
Date: Wed, 17 Apr 2019 00:31:12 +0000
Subject: [PATCH] [kernel] Add shim from old zbi_cpu_config_t to
 SystemTopology.

When we handle the zbi_cpu_config_t type on ARM systems we will now
convert it to a new flat topology and feed it into the topology
subsystem. This allows us to have the Topology subsystem as the source
of truth for topology logic on all platforms. Eventually we will remove
the zbi_cpu_config_t alltogether.

ZX-3068
Test: Booted and "k ut all" on:
*Qemu arm
*Astro
*Vim2

Change-Id: Ia22eb29146b56e030d540dd92e469e48f0726e4b
---
 zircon/kernel/arch/arm64/BUILD.gn             |   4 -
 .../kernel/arch/arm64/include/arch/arm64/mp.h |   2 -
 zircon/kernel/arch/arm64/mp.cpp               |  67 +------
 .../topology/include/lib/system-topology.h    |   4 +-
 .../kernel/lib/topology/system-topology.cpp   |  39 ++--
 zircon/kernel/params.gni                      |   3 -
 zircon/kernel/platform/generic-arm/BUILD.gn   |   1 -
 .../kernel/platform/generic-arm/platform.cpp  | 188 +++++++++++-------
 8 files changed, 146 insertions(+), 162 deletions(-)

diff --git a/zircon/kernel/arch/arm64/BUILD.gn b/zircon/kernel/arch/arm64/BUILD.gn
index c4f0d94bf0e..964909f36c0 100644
--- a/zircon/kernel/arch/arm64/BUILD.gn
+++ b/zircon/kernel/arch/arm64/BUILD.gn
@@ -61,10 +61,6 @@ if (current_toolchain == default_toolchain) {
   }
 
   source_set("arm64") {
-    defines = [
-      "SMP_CPU_MAX_CLUSTER_CPUS=$smp_max_cpus",
-      "SMP_CPU_MAX_CLUSTERS=$smp_max_clusters",
-    ]
     sources = [
       "arch.cpp",
       "asm.S",
diff --git a/zircon/kernel/arch/arm64/include/arch/arm64/mp.h b/zircon/kernel/arch/arm64/include/arch/arm64/mp.h
index 27a78e70894..1125dab3cdf 100644
--- a/zircon/kernel/arch/arm64/include/arch/arm64/mp.h
+++ b/zircon/kernel/arch/arm64/include/arch/arm64/mp.h
@@ -107,8 +107,6 @@ static inline uint arch_cpu_num_to_cpu_id(uint cpu) {
     return arm64_cpu_cpu_ids[cpu];
 }
 
-cpu_num_t arch_mpid_to_cpu_num(uint cluster, uint cpu);
-
 #define READ_PERCPU_FIELD32(field) \
     arm64_read_percpu_u32(offsetof(struct arm64_percpu, field))
 
diff --git a/zircon/kernel/arch/arm64/mp.cpp b/zircon/kernel/arch/arm64/mp.cpp
index e3e4d862bc2..1a633c00d8b 100644
--- a/zircon/kernel/arch/arm64/mp.cpp
+++ b/zircon/kernel/arch/arm64/mp.cpp
@@ -27,11 +27,6 @@ struct MpidCpuidPair {
     uint cpu_id;
 };
 
-// TODO(ZX-3068) Switch completely to list and remove map.
-bool use_cpu_map = true;
-
-// map of cluster/cpu to cpu_id
-uint arm64_cpu_map[SMP_CPU_MAX_CLUSTERS][SMP_CPU_MAX_CLUSTER_CPUS] = {{0}};
 MpidCpuidPair arm64_cpu_list[SMP_MAX_CPUS];
 size_t arm64_cpu_list_count = 0;
 
@@ -47,34 +42,6 @@ uint arm_num_cpus = 1;
 // per cpu structures, each cpu will point to theirs using the x18 register
 arm64_percpu arm64_percpu_array[SMP_MAX_CPUS];
 
-// initializes cpu_map and arm_num_cpus
-void arch_init_cpu_map(uint cluster_count, const uint* cluster_cpus) {
-    ASSERT(cluster_count <= SMP_CPU_MAX_CLUSTERS);
-
-    // assign cpu_ids sequentially
-    uint cpu_id = 0;
-    for (uint cluster = 0; cluster < cluster_count; cluster++) {
-        uint cpus = *cluster_cpus++;
-        ASSERT(cpus <= SMP_CPU_MAX_CLUSTER_CPUS);
-        for (uint cpu = 0; cpu < cpus; cpu++) {
-            // given cluster:cpu, translate to global cpu id
-            arm64_cpu_map[cluster][cpu] = cpu_id;
-
-            // given global gpu_id, translate to cluster and cpu number within cluster
-            arm64_cpu_cluster_ids[cpu_id] = cluster;
-            arm64_cpu_cpu_ids[cpu_id] = cpu;
-
-            // set the per cpu structure's cpu id
-            arm64_percpu_array[cpu_id].cpu_num = cpu_id;
-
-            cpu_id++;
-        }
-    }
-    arm_num_cpus = cpu_id;
-    use_cpu_map = true;
-    smp_mb();
-}
-
 void arch_register_mpid(uint cpu_id, uint64_t mpid) {
     // TODO(ZX-3068) transition off of these maps to the topology.
     arm64_cpu_cluster_ids[cpu_id] = (mpid & 0xFF00) >> MPIDR_AFF1_SHIFT; // "cluster" here is AFF1.
@@ -83,37 +50,25 @@ void arch_register_mpid(uint cpu_id, uint64_t mpid) {
     arm64_percpu_array[cpu_id].cpu_num = cpu_id;
 
     arm64_cpu_list[arm64_cpu_list_count++] = {.mpid = mpid, .cpu_id = cpu_id};
-
-    use_cpu_map = false;
 }
 
 // do the 'slow' lookup by mpidr to cpu number
 static uint arch_curr_cpu_num_slow() {
     uint64_t mpidr = __arm_rsr64("mpidr_el1");
-    if (use_cpu_map) {
-        uint cluster = (mpidr & MPIDR_AFF1_MASK) >> MPIDR_AFF1_SHIFT;
-        uint cpu = (mpidr & MPIDR_AFF0_MASK) >> MPIDR_AFF0_SHIFT;
-
-        return arm64_cpu_map[cluster][cpu];
-    } else {
-        mpidr &= kMpidAffMask;
-        for (size_t i = 0; i < arm64_cpu_list_count; ++i) {
-            if (arm64_cpu_list[i].mpid == mpidr) {
-                return arm64_cpu_list[i].cpu_id;
-            }
-        }
-
-        // The only time we shouldn't find a cpu is when the list isn't
-        // defined yet during early boot, in this case the only processor up is 0
-        // so returning 0 is correct.
-        DEBUG_ASSERT(arm64_cpu_list_count == 0);
+    mpidr &= kMpidAffMask;
 
-        return 0;
+    for (size_t i = 0; i < arm64_cpu_list_count; ++i) {
+        if (arm64_cpu_list[i].mpid == mpidr) {
+            return arm64_cpu_list[i].cpu_id;
+        }
     }
-}
 
-cpu_num_t arch_mpid_to_cpu_num(uint cluster, uint cpu) {
-    return arm64_cpu_map[cluster][cpu];
+    // The only time we shouldn't find a cpu is when the list isn't
+    // defined yet during early boot, in this case the only processor up is 0
+    // so returning 0 is correct.
+    DEBUG_ASSERT(arm64_cpu_list_count == 0);
+
+    return 0;
 }
 
 void arch_prepare_current_cpu_idle_state(bool idle) {
diff --git a/zircon/kernel/lib/topology/include/lib/system-topology.h b/zircon/kernel/lib/topology/include/lib/system-topology.h
index e30161c0b57..299cb1df795 100644
--- a/zircon/kernel/lib/topology/include/lib/system-topology.h
+++ b/zircon/kernel/lib/topology/include/lib/system-topology.h
@@ -47,7 +47,7 @@ public:
     // we MUST redesign this process to consider concurrent readers.
     // Returns ZX_ERR_ALREADY_EXISTS if state already set or ZX_ERR_INVALID_ARGS if provided graph
     // fails validation.
-    zx_status_t Update(zbi_topology_node_t* nodes, size_t count);
+    zx_status_t Update(const zbi_topology_node_t* nodes, size_t count);
 
     // Provides iterable container of pointers to all processor nodes.
     IterableProcessors processors() const {
@@ -75,7 +75,7 @@ private:
     //   - there are no cycles.
     //   - It is stored in a "depth first" ordering, with parents adjacent to
     //   their children.
-    bool Validate(zbi_topology_node_t* nodes, int count) const;
+    bool Validate(const zbi_topology_node_t* nodes, int count) const;
 
     fbl::unique_ptr<Node[]> nodes_;
     fbl::Vector<Node*> processors_;
diff --git a/zircon/kernel/lib/topology/system-topology.cpp b/zircon/kernel/lib/topology/system-topology.cpp
index 17a9fbd6cec..cf9dbf216bb 100644
--- a/zircon/kernel/lib/topology/system-topology.cpp
+++ b/zircon/kernel/lib/topology/system-topology.cpp
@@ -22,26 +22,30 @@ zx_status_t GrowVector(size_t new_size, fbl::Vector<T>* vector, fbl::AllocChecke
 
 } // namespace
 
-zx_status_t Graph::Update(zbi_topology_node_t* flat_nodes, size_t count) {
-    if (flat_nodes == nullptr || count == 0 || !Validate(flat_nodes, static_cast<int>(count))) {
-        return ZX_ERR_INVALID_ARGS;
-    }
-
+zx_status_t Graph::Update(const zbi_topology_node_t* flat_nodes, size_t count) {
     if (nodes_.get() != nullptr) {
         return ZX_ERR_ALREADY_EXISTS;
     }
 
+    if (flat_nodes == nullptr || count == 0 || !Validate(flat_nodes, static_cast<int>(count))) {
+        return ZX_ERR_INVALID_ARGS;
+    }
+
     fbl::AllocChecker checker;
-    nodes_.reset(new (&checker) Node[count]{{}});
+    fbl::unique_ptr<Node[]> nodes(new (&checker) Node[count]{{}});
     if (!checker.check()) {
         return ZX_ERR_NO_MEMORY;
     }
 
+    // Create local instances, if successful we will move them to the Graph's fields.
+    fbl::Vector<Node*> processors;
+    fbl::Vector<Node*> processors_by_logical_id;
+
     Node* node = nullptr;
-    zbi_topology_node_t* flat_node = nullptr;
+    const zbi_topology_node_t* flat_node = nullptr;
     for (size_t i = 0; i < count; ++i) {
         flat_node = &flat_nodes[i];
-        node = &nodes_[i];
+        node = &nodes[i];
 
         node->entity_type = flat_node->entity_type;
 
@@ -50,18 +54,16 @@ zx_status_t Graph::Update(zbi_topology_node_t* flat_nodes, size_t count) {
         case ZBI_TOPOLOGY_ENTITY_PROCESSOR:
             node->entity.processor = flat_node->entity.processor;
 
-            processors_.push_back(node, &checker);
+            processors.push_back(node, &checker);
             if (!checker.check()) {
-                nodes_.reset(nullptr);
                 return ZX_ERR_NO_MEMORY;
             }
 
             for (int i = 0; i < node->entity.processor.logical_id_count; ++i) {
                 const auto index = node->entity.processor.logical_ids[i];
-                GrowVector(index + 1, &processors_by_logical_id_, &checker);
-                processors_by_logical_id_[index] = node;
+                GrowVector(index + 1, &processors_by_logical_id, &checker);
+                processors_by_logical_id[index] = node;
                 if (!checker.check()) {
-                    nodes_.reset(nullptr);
                     return ZX_ERR_NO_MEMORY;
                 }
             }
@@ -82,19 +84,22 @@ zx_status_t Graph::Update(zbi_topology_node_t* flat_nodes, size_t count) {
             ZX_DEBUG_ASSERT_MSG(flat_node->parent_index >= 0 && flat_node->parent_index < count,
                                 "parent_index out of range: %u\n", flat_node->parent_index);
 
-            node->parent = &nodes_[flat_node->parent_index];
+            node->parent = &nodes[flat_node->parent_index];
             node->parent->children.push_back(node, &checker);
             if (!checker.check()) {
-                nodes_.reset(nullptr);
                 return ZX_ERR_NO_MEMORY;
             }
         }
     }
 
+    nodes_.swap(nodes);
+    processors_ = std::move(processors);
+    processors_by_logical_id_ = std::move(processors_by_logical_id);
+
     return ZX_OK;
 }
 
-bool Graph::Validate(zbi_topology_node_t* nodes, int count) const {
+bool Graph::Validate(const zbi_topology_node_t* nodes, int count) const {
     uint16_t parents[kMaxTopologyDepth];
     for (size_t i = 0; i < kMaxTopologyDepth; ++i) {
         parents[i] = ZBI_TOPOLOGY_NO_PARENT;
@@ -103,7 +108,7 @@ bool Graph::Validate(zbi_topology_node_t* nodes, int count) const {
     uint8_t current_type = ZBI_TOPOLOGY_ENTITY_UNDEFINED;
     int current_depth = 0;
 
-    zbi_topology_node_t* node;
+    const zbi_topology_node_t* node;
     for (int current_index = count - 1; current_index >= 0; current_index--) {
         node = &nodes[current_index];
 
diff --git a/zircon/kernel/params.gni b/zircon/kernel/params.gni
index 9e0acfed8f4..55a50d2e8c6 100644
--- a/zircon/kernel/params.gni
+++ b/zircon/kernel/params.gni
@@ -7,10 +7,7 @@ declare_args() {
   smp_max_cpus = 32
 
   if (current_cpu == "arm64") {
-    # Maximum number of CPU clusters the kernel will support.
-    # The kernel will panic at boot on hardware with more clusters.
     smp_max_cpus = 16
-    smp_max_clusters = 2
   }
 
   # Virtual address where the kernel is mapped statically.  This is the
diff --git a/zircon/kernel/platform/generic-arm/BUILD.gn b/zircon/kernel/platform/generic-arm/BUILD.gn
index 0aa57e83867..da0558aa574 100644
--- a/zircon/kernel/platform/generic-arm/BUILD.gn
+++ b/zircon/kernel/platform/generic-arm/BUILD.gn
@@ -8,7 +8,6 @@ source_set("generic-arm") {
   sources = [
     "platform.cpp",
   ]
-  defines = [ "SMP_CPU_MAX_CLUSTERS=$smp_max_clusters" ]
   deps = [
     "$zx/kernel/dev/hdcp/amlogic_s912",
     "$zx/kernel/dev/hw_rng",
diff --git a/zircon/kernel/platform/generic-arm/platform.cpp b/zircon/kernel/platform/generic-arm/platform.cpp
index 97e4293ad1e..220ad31a389 100644
--- a/zircon/kernel/platform/generic-arm/platform.cpp
+++ b/zircon/kernel/platform/generic-arm/platform.cpp
@@ -69,9 +69,6 @@ static zbi_header_t* zbi_root = nullptr;
 
 static zbi_nvram_t lastlog_nvram;
 
-static uint cpu_cluster_count = 0;
-static uint cpu_cluster_cpus[SMP_CPU_MAX_CLUSTERS] = {0};
-
 static bool halt_on_panic = false;
 static bool uart_disabled = false;
 
@@ -92,9 +89,6 @@ static size_t mexec_zbi_length = 0;
 
 static volatile int panic_started;
 
-// TODO(ZX-3068) This is temporary until we fully deprecate ZBI_CPU_CONFIG.
-static bool use_topology = false;
-
 static constexpr bool kProcessZbiEarly = true;
 
 static void halt_other_cpus(void) {
@@ -221,38 +215,6 @@ static void topology_cpu_init(void) {
     }
 }
 
-static void platform_cpu_init(void) {
-    for (uint cluster = 0; cluster < cpu_cluster_count; cluster++) {
-        for (uint cpu = 0; cpu < cpu_cluster_cpus[cluster]; cpu++) {
-            if (cluster != 0 || cpu != 0) {
-                const uint cpu_num = arch_mpid_to_cpu_num(cluster, cpu);
-                const uint64_t mpid = ARM64_MPID(cluster, cpu);
-
-                // create a stack for the cpu we're about to start
-                zx_status_t status = arm64_create_secondary_stack(cpu_num, mpid);
-
-                DEBUG_ASSERT(status == ZX_OK);
-
-                // start the cpu
-                status = platform_start_cpu(mpid);
-
-                if (status != ZX_OK) {
-                    // TODO(maniscalco): Is continuing really the right thing to do here?
-
-                    // start failed, free the stack
-                    zx_status_t status = arm64_free_secondary_stack(cpu_num);
-                    DEBUG_ASSERT(status == ZX_OK);
-                    continue;
-                }
-
-                // the cpu booted
-                //
-                // bootstrap thread is now responsible for freeing its stack
-            }
-        }
-    }
-}
-
 static inline bool is_zbi_container(void* addr) {
     DEBUG_ASSERT(addr);
 
@@ -335,16 +297,7 @@ static zbi_result_t process_zbi_item_early(zbi_header_t* item,
         save_mexec_zbi(item);
         break;
     }
-    case ZBI_TYPE_CPU_CONFIG: {
-        zbi_cpu_config_t* cpu_config = reinterpret_cast<zbi_cpu_config_t*>(payload);
-        cpu_cluster_count = cpu_config->cluster_count;
-        for (uint32_t i = 0; i < cpu_cluster_count; i++) {
-            cpu_cluster_cpus[i] = cpu_config->clusters[i].cpu_count;
-        }
-        arch_init_cpu_map(cpu_cluster_count, cpu_cluster_cpus);
-        save_mexec_zbi(item);
-        break;
-    }
+
     case ZBI_TYPE_NVRAM: {
         zbi_nvram_t* nvram = reinterpret_cast<zbi_nvram_t*>(payload);
         memcpy(&lastlog_nvram, nvram, sizeof(lastlog_nvram));
@@ -359,40 +312,125 @@ static zbi_result_t process_zbi_item_early(zbi_header_t* item,
     return ZBI_RESULT_OK;
 }
 
+static constexpr zbi_topology_node_t fallback_topology = {
+    .entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR,
+    .parent_index = ZBI_TOPOLOGY_NO_PARENT,
+    .entity = {
+        .processor = {
+            .logical_ids = {0},
+            .logical_id_count = 1,
+            .flags = 0,
+            .architecture = ZBI_TOPOLOGY_ARCH_ARM,
+            .architecture_info = {
+                .arm = {
+                    .cluster_1_id = 0,
+                    .cluster_2_id = 0,
+                    .cluster_3_id = 0,
+                    .cpu_id = 0,
+                    .gic_id = 0,
+                }
+            }
+        }
+    }
+};
+
+static void init_topology(zbi_topology_node_t* nodes, size_t node_count) {
+    auto result = system_topology::GetMutableSystemTopology()
+            .Update(nodes, node_count);
+    if (result != ZX_OK) {
+        printf("Failed to initialize system topology! error: %d \n",
+               result);
+
+        // Try to fallback to a topology of just this processor.
+        result = system_topology::GetMutableSystemTopology()
+                .Update(&fallback_topology, 1);
+        ASSERT(result == ZX_OK);
+    }
+
+    arch_set_num_cpus(static_cast<uint>(
+        system_topology::GetSystemTopology().processor_count()));
+
+    // TODO(ZX-3068) Print the whole topology of the system.
+    if (LK_DEBUGLEVEL >= INFO) {
+        for (auto* proc :
+             system_topology::GetSystemTopology().processors()) {
+            auto& info = proc->entity.processor.architecture_info.arm;
+            dprintf(INFO, "System topology: CPU %u:%u:%u:%u\n",
+                    info.cluster_3_id,
+                    info.cluster_2_id,
+                    info.cluster_1_id,
+                    info.cpu_id);
+        }
+    }
+}
+
 // Called after heap is up, but before multithreading.
 static zbi_result_t process_zbi_item_late(zbi_header_t* item,
                                           void* payload, void*) {
     switch (item->type) {
+    case ZBI_TYPE_CPU_CONFIG: {
+        zbi_cpu_config_t* cpu_config = reinterpret_cast<zbi_cpu_config_t*>(payload);
+
+        // Convert old zbi_cpu_config into zbi_topology structure.
+
+        // Allocate some memory to work in.
+        size_t node_count = 0;
+        for (size_t cluster = 0; cluster < cpu_config->cluster_count; cluster++) {
+            // Each cluster will get a node.
+            node_count++;
+            node_count += cpu_config->clusters[cluster].cpu_count;
+        }
+
+        fbl::AllocChecker checker;
+        auto flat_topology = ktl::unique_ptr<zbi_topology_node_t[]> {
+            new (&checker) zbi_topology_node_t[node_count]};
+        if (!checker.check()) {
+            return ZBI_RESULT_ERROR;
+        }
+
+        // Initialize to 0.
+        memset(flat_topology.get(), 0, sizeof(zbi_topology_node_t) * node_count);
+
+        // Create topology structure.
+        size_t flat_index = 0;
+        uint16_t logical_id = 0;
+        for (size_t cluster = 0; cluster < cpu_config->cluster_count; cluster++) {
+            const auto cluster_index = flat_index;
+            auto& node = flat_topology.get()[flat_index++];
+            node.entity_type = ZBI_TOPOLOGY_ENTITY_CLUSTER;
+            node.parent_index = ZBI_TOPOLOGY_NO_PARENT;
+
+            // We don't have this data so it is a guess that little cores are
+            // first.
+            node.entity.cluster.performance_class = static_cast<uint8_t>(cluster);
+
+            for (size_t i = 0; i < cpu_config->clusters[cluster].cpu_count; i++) {
+                auto& node = flat_topology.get()[flat_index++];
+                node.entity_type = ZBI_TOPOLOGY_ENTITY_PROCESSOR;
+                node.parent_index = static_cast<uint16_t>(cluster_index);
+                node.entity.processor.logical_id_count = 1;
+                node.entity.processor.logical_ids[0] = logical_id++;
+                node.entity.processor.architecture = ZBI_TOPOLOGY_ARCH_ARM;
+                node.entity.processor.architecture_info.arm.cluster_1_id =
+                        static_cast<uint8_t>(cluster);
+                node.entity.processor.architecture_info.arm.cpu_id = static_cast<uint8_t>(i);
+            }
+        }
+        DEBUG_ASSERT(flat_index == node_count);
+
+        // Initialize topology subsystem.
+        init_topology(flat_topology.get(), node_count);
+        save_mexec_zbi(item);
+        break;
+    }
     case ZBI_TYPE_CPU_TOPOLOGY: {
         const int node_count = item->length / item->extra;
 
         zbi_topology_node_t* nodes =
             reinterpret_cast<zbi_topology_node_t*>(payload);
 
-        auto result = system_topology::GetMutableSystemTopology()
-                          .Update(nodes, node_count);
-        if (result != ZX_OK) {
-            printf("Failed to initialize system topology! error: %d \n",
-                   result);
-        } else {
-            use_topology = true;
-            arch_set_num_cpus(static_cast<uint>(
-                system_topology::GetSystemTopology().processor_count()));
-
-            // TODO(ZX-3068) Print the whole topology of the system.
-            if (LK_DEBUGLEVEL >= INFO) {
-                for (auto* proc :
-                     system_topology::GetSystemTopology().processors()) {
-                    auto& info = proc->entity.processor.architecture_info.arm;
-                    dprintf(INFO, "System topology: CPU %u:%u:%u:%u\n",
-                            info.cluster_3_id,
-                            info.cluster_2_id,
-                            info.cluster_1_id,
-                            info.cpu_id);
-                }
-            }
-        }
-
+        init_topology(nodes, node_count);
+        save_mexec_zbi(item);
         break;
     }
     }
@@ -500,11 +538,7 @@ LK_INIT_HOOK(platform_init_pre_thread, platform_init_pre_thread,
              LK_INIT_LEVEL_THREADING - 1)
 
 void platform_init(void) {
-    if (use_topology) {
-        topology_cpu_init();
-    } else {
-        platform_cpu_init();
-    }
+    topology_cpu_init();
 }
 
 // after the fact create a region to reserve the peripheral map(s)
-- 
GitLab