From 20e2e39fcc044a633d321ff5afaa4208d0d26f0e Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Sat, 28 May 2022 21:14:08 +0100 Subject: [PATCH] Kernel: Expose size of L1 data/instruction, L2, and L3 CPU caches :^) These are added as properties of the "caches" object to each processor, if available. --- Kernel/Arch/x86/ProcessorInfo.h | 16 +++++++++ Kernel/Arch/x86/common/ProcessorInfo.cpp | 46 ++++++++++++++++++++++++ Kernel/GlobalProcessExposed.cpp | 22 ++++++++++++ 3 files changed, 84 insertions(+) diff --git a/Kernel/Arch/x86/ProcessorInfo.h b/Kernel/Arch/x86/ProcessorInfo.h index 19392ae2ec..339013678a 100644 --- a/Kernel/Arch/x86/ProcessorInfo.h +++ b/Kernel/Arch/x86/ProcessorInfo.h @@ -21,6 +21,11 @@ class ProcessorInfo { public: ProcessorInfo(Processor const& processor); + struct Cache { + u64 size; + u64 line_size; + }; + StringView vendor_id_string() const { return m_vendor_id_string->view(); } StringView hypervisor_vendor_id_string() const { return m_hypervisor_vendor_id_string->view(); } StringView brand_string() const { return m_brand_string->view(); } @@ -30,6 +35,10 @@ public: u32 stepping() const { return m_stepping; } u32 type() const { return m_type; } u32 apic_id() const { return m_apic_id; } + Optional const& l1_data_cache() const { return m_l1_data_cache; } + Optional const& l1_instruction_cache() const { return m_l1_instruction_cache; } + Optional const& l2_cache() const { return m_l2_cache; } + Optional const& l3_cache() const { return m_l3_cache; } void set_apic_id(u32 apic_id) { m_apic_id = apic_id; } @@ -39,6 +48,8 @@ private: static NonnullOwnPtr build_brand_string(); static NonnullOwnPtr build_features_string(Processor const&); + void populate_cache_sizes(); + NonnullOwnPtr m_vendor_id_string; NonnullOwnPtr m_hypervisor_vendor_id_string; NonnullOwnPtr m_brand_string; @@ -48,6 +59,11 @@ private: u32 m_stepping { 0 }; u32 m_type { 0 }; u32 m_apic_id { 0 }; + + Optional m_l1_data_cache; + Optional m_l1_instruction_cache; + Optional m_l2_cache; + Optional m_l3_cache; }; } diff --git a/Kernel/Arch/x86/common/ProcessorInfo.cpp b/Kernel/Arch/x86/common/ProcessorInfo.cpp index 2706a681eb..3743e94c0f 100644 --- a/Kernel/Arch/x86/common/ProcessorInfo.cpp +++ b/Kernel/Arch/x86/common/ProcessorInfo.cpp @@ -36,6 +36,8 @@ ProcessorInfo::ProcessorInfo(Processor const& processor) m_display_family = family; m_display_model = model; } + + populate_cache_sizes(); } static void emit_u32(StringBuilder& builder, u32 value) @@ -110,4 +112,48 @@ NonnullOwnPtr ProcessorInfo::build_features_string(Processor const& pro return KString::must_create(builder.string_view()); } +void ProcessorInfo::populate_cache_sizes() +{ + u32 max_extended_leaf = CPUID(0x80000000).eax(); + + if (max_extended_leaf < 0x80000005) + return; + + auto l1_cache_info = CPUID(0x80000005); + + // NOTE: Except for L2, these are not available on Intel CPUs in this form and return 0 for each register in that case. + if (l1_cache_info.ecx() != 0) { + m_l1_data_cache = Cache { + .size = ((l1_cache_info.ecx() >> 24) & 0xff) * KiB, + .line_size = l1_cache_info.ecx() & 0xff, + }; + } + + if (l1_cache_info.edx() != 0) { + m_l1_instruction_cache = Cache { + .size = ((l1_cache_info.edx() >> 24) & 0xff) * KiB, + .line_size = l1_cache_info.edx() & 0xff, + }; + } + + if (max_extended_leaf < 0x80000006) + return; + + auto l2_l3_cache_info = CPUID(0x80000006); + + if (l2_l3_cache_info.ecx() != 0) { + m_l2_cache = Cache { + .size = ((l2_l3_cache_info.ecx() >> 16) & 0xffff) * KiB, + .line_size = l2_l3_cache_info.ecx() & 0xff, + }; + } + + if (l2_l3_cache_info.edx() != 0) { + m_l3_cache = Cache { + .size = (static_cast((l2_l3_cache_info.edx() >> 18)) & 0x3fff) * 512 * KiB, + .line_size = l2_l3_cache_info.edx() & 0xff, + }; + } +} + } diff --git a/Kernel/GlobalProcessExposed.cpp b/Kernel/GlobalProcessExposed.cpp index 01bdb5f2d9..025b3ea8be 100644 --- a/Kernel/GlobalProcessExposed.cpp +++ b/Kernel/GlobalProcessExposed.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2021, Liav A. + * Copyright (c) 2022, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause */ @@ -639,6 +640,27 @@ private: TRY(obj.add("type", info.type())); TRY(obj.add("brand", info.brand_string())); + auto caches = TRY(obj.add_object("caches")); + + auto add_cache_info = [&](StringView name, ProcessorInfo::Cache const& cache) -> ErrorOr { + auto cache_object = TRY(caches.add_object(name)); + TRY(cache_object.add("size", cache.size)); + TRY(cache_object.add("line_size", cache.line_size)); + TRY(cache_object.finish()); + return {}; + }; + + if (info.l1_data_cache().has_value()) + TRY(add_cache_info("l1_data", *info.l1_data_cache())); + if (info.l1_data_cache().has_value()) + TRY(add_cache_info("l1_instruction", *info.l1_instruction_cache())); + if (info.l1_data_cache().has_value()) + TRY(add_cache_info("l2", *info.l2_cache())); + if (info.l1_data_cache().has_value()) + TRY(add_cache_info("l3", *info.l3_cache())); + + TRY(caches.finish()); + TRY(obj.finish()); return {}; }));