[kernel][lib/arch] Work around GCC bug in BootCpuidIo::GetLeaf template
GCC fails to honor the `section` attribute when in a COMDAT context
like a template instantiation. There doesn't seem to be any way to
get GCC to emit the right thing. So in the GCC build just skip the
preinitialized special section and do on-demand initialization for
template instantiations. The explicitly specialization
instantiations used from assembly can still use the special section
since those variable definitions are outside any COMDAT context.
Bug: 27083, 64109
Change-Id: Ie4d4ff29c5c1c8b3a6c7d8115addbac9d677029d
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/449394
Commit-Queue: Joshua Seaton <joshuaseaton@google.com>
Reviewed-by: Joshua Seaton <joshuaseaton@google.com>
Testability-Review: Roland McGrath <mcgrathr@google.com>
Fuchsia-Auto-Submit: Roland McGrath <mcgrathr@google.com>
diff --git a/zircon/kernel/lib/arch/include/lib/arch/x86/boot-cpuid.h b/zircon/kernel/lib/arch/x86/include/lib/arch/x86/boot-cpuid.h
similarity index 82%
rename from zircon/kernel/lib/arch/include/lib/arch/x86/boot-cpuid.h
rename to zircon/kernel/lib/arch/x86/include/lib/arch/x86/boot-cpuid.h
index ff8804a..a2eb7cf 100644
--- a/zircon/kernel/lib/arch/include/lib/arch/x86/boot-cpuid.h
+++ b/zircon/kernel/lib/arch/x86/include/lib/arch/x86/boot-cpuid.h
@@ -32,6 +32,11 @@
// populate the data. That function requires only the basic machine stack for
// its call and return, and doesn't need the full C++ ABI to be available yet.
+// TODO(fxbug.dev/64109): <cpuid.h> lacks a proper multiple inclusion guard!
+#ifndef __cpuid
+#include <cpuid.h>
+#endif
+
#include <type_traits>
namespace arch {
@@ -60,6 +65,7 @@
// The underlying instantiation is indexed by leaf and subleaf.
template <uint32_t Leaf, uint32_t Subleaf = 0>
const CpuidIo* GetLeaf() const {
+#ifdef __clang__
// The CpuidIo object for each instantiation goes into a special section
// that the InitializeBootCpuid() function processes when called at
// startup. Each entry starts at compile time with the leaf and subleaf
@@ -73,6 +79,26 @@
static_assert(std::is_same_v<decltype(CpuidIo{}.values_), uint32_t[4]>);
[[gnu::section("BootCpuid")]] alignas(uint32_t) static CpuidIo gCpuidIo =
internal::kBootCpuidInitializer<Leaf, Subleaf>;
+#else
+ // TODO(fxbug.dev/27083): GCC doesn't honor the section attribute in a
+ // COMDAT context. Instead, just do on-demand initialization right here.
+ // This bloats the code at each use and is less efficient than simply
+ // preloading all the data at startup via the special section hack.
+ static CpuidIo gCpuidIo = []() {
+ using MaxLeaf = std::conditional_t<
+ (Leaf < CpuidMaximumHypervisorLeaf::kLeaf), CpuidMaximumLeaf,
+ std::conditional_t<(Leaf < CpuidMaximumExtendedLeaf::kLeaf), CpuidMaximumHypervisorLeaf,
+ CpuidMaximumExtendedLeaf>>;
+ if (Leaf > BootCpuidIo{}.Read<MaxLeaf>().reg_value()) {
+ return CpuidIo{};
+ }
+ CpuidIo data;
+ __cpuid_count(Leaf, Subleaf, //
+ data.values_[CpuidIo::kEax], data.values_[CpuidIo::kEbx],
+ data.values_[CpuidIo::kEcx], data.values_[CpuidIo::kEdx]);
+ return data;
+ }();
+#endif // __clang__
return &gCpuidIo;
}