diff --git a/kernel/include/init/acpi.h b/kernel/include/acpi/acpi.h similarity index 100% rename from kernel/include/init/acpi.h rename to kernel/include/acpi/acpi.h diff --git a/kernel/include/init/apic.h b/kernel/include/acpi/madt.h similarity index 99% rename from kernel/include/init/apic.h rename to kernel/include/acpi/madt.h index fe23166..b31b6b1 100644 --- a/kernel/include/init/apic.h +++ b/kernel/include/acpi/madt.h @@ -1,6 +1,6 @@ #pragma once -#include "init/acpi.h" +#include "acpi/acpi.h" #include "memory/pointer.h" #include "util/number.h" diff --git a/kernel/include/apic/timer.h b/kernel/include/apic/timer.h new file mode 100644 index 0000000..b0cffb0 --- /dev/null +++ b/kernel/include/apic/timer.h @@ -0,0 +1,19 @@ +#pragma once + +#include "acpi/madt.h" +#include "util/optional.h" + +namespace apic { + +struct lapic_timer_calibration_t { + uint64_t tsc_frequency_hz; +}; + +optional calibrateTimer(const acpi::Madt& madt); + +// TODO: when dynamic allocation and lists are available +// struct Timer {}; +// +// Timer getCoreTimerInstance(); + +} // namespace apic diff --git a/kernel/include/util/cpuid.h b/kernel/include/util/cpuid.h new file mode 100644 index 0000000..a40d2b8 --- /dev/null +++ b/kernel/include/util/cpuid.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "util/optional.h" + +struct cpuid_result_t { + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; +}; + +inline optional cpuid(uint32_t leaf, uint32_t subleaf = 0) { + cpuid_result_t result{}; + if (__get_cpuid_count(leaf, subleaf, &result.eax, &result.ebx, &result.ecx, &result.edx) == 0) { + return {}; + } + return result; +} diff --git a/kernel/include/util/function.h b/kernel/include/util/function.h index 2b5ae0b..b346920 100644 --- a/kernel/include/util/function.h +++ b/kernel/include/util/function.h @@ -1,5 +1,7 @@ #pragma once +#include "util/utility.h" + template struct overloaded : Ts... { using Ts::operator()...; @@ -7,3 +9,17 @@ struct overloaded : Ts... { template overloaded(Ts...) -> overloaded; + +template +auto partial(Func func, BoundArgs... bound_args) { + return [func, bound_args...](auto&&... call_args) { + return func(bound_args..., forward(call_args)...); + }; +} + +template +auto construct() { + return [](auto&&... args) { + return Type{forward(args)...}; + }; +} diff --git a/kernel/include/util/optional.h b/kernel/include/util/optional.h index 183b45d..e69f107 100644 --- a/kernel/include/util/optional.h +++ b/kernel/include/util/optional.h @@ -9,6 +9,9 @@ // instead of . The CrackOS3 value_or_panic() helper is omitted because // there is no global panic() in this tree yet. +struct nullopt_t {}; +inline constexpr nullopt_t nullopt{}; + template struct optional { private: @@ -26,6 +29,7 @@ public: using value_type = T; constexpr optional() : initialized(false) {} + constexpr optional(nullopt_t) : initialized(false) {} optional(const T& value) : initialized(true) { new (buffer) T(value); } @@ -135,10 +139,45 @@ public: return !initialized; } - template - optional map(Func func, ArgT... args) { + template + auto map(Func&& func) -> optional { if (initialized) { - return func(value(), forward(args)...); + return func(value()); + } else { + return {}; + } + } + + template + auto map(Func&& func) const -> optional { + if (initialized) { + return func(value()); + } else { + return {}; + } + } + + template + optional or_else(Func&& func) const { + if (initialized) { + return *this; + } + return func(); + } + + template + auto bind(Func&& func) -> decltype(func(value())) { + if (initialized) { + return func(value()); + } else { + return {}; + } + } + + template + auto bind(Func&& func) const -> decltype(func(value())) { + if (initialized) { + return func(value()); } else { return {}; } diff --git a/kernel/include/util/utility.h b/kernel/include/util/utility.h index bf4c3bc..ce1090e 100644 --- a/kernel/include/util/utility.h +++ b/kernel/include/util/utility.h @@ -18,6 +18,11 @@ struct remove_reference { template using remove_reference_t = typename remove_reference::type; +template +inline constexpr bool is_void_v = false; +template<> +inline constexpr bool is_void_v = true; + template constexpr remove_reference_t&& move(T&& value) { return static_cast&&>(value); diff --git a/kernel/src/apic/timer.cpp b/kernel/src/apic/timer.cpp new file mode 100644 index 0000000..cbc3d38 --- /dev/null +++ b/kernel/src/apic/timer.cpp @@ -0,0 +1,55 @@ +#include "apic/timer.h" + +#include "util/cpuid.h" +#include "util/function.h" + +namespace { + +constexpr uint32_t feature_info_leaf = 0x1; +constexpr uint32_t tsc_crystal_ratio_leaf = 0x15; +constexpr uint32_t processor_frequency_leaf = 0x16; + +constexpr uint32_t feature_edx_tsc = 1u << 4; + +bool isTscSupported() { + return cpuid(feature_info_leaf) + .map([](const cpuid_result_t& features) { + return (features.edx & feature_edx_tsc) != 0; + }) + .value_or(false); +} + +optional tscFrequencyFromCrystalRatio() { + return cpuid(tsc_crystal_ratio_leaf).bind([](const cpuid_result_t& ratio) -> optional { + uint64_t denominator = ratio.eax; + uint64_t numerator = ratio.ebx; + uint64_t crystal_frequency_hz = ratio.ecx; + if (denominator == 0 || numerator == 0 || crystal_frequency_hz == 0) { + return nullopt; + } + return crystal_frequency_hz * numerator / denominator; + }); +} + +optional tscFrequencyFromProcessorBase() { + return cpuid(processor_frequency_leaf).bind([](const cpuid_result_t& frequency_info) -> optional { + uint64_t base_frequency_mhz = frequency_info.eax & 0xFFFF; + if (base_frequency_mhz == 0) { + return nullopt; + } + return base_frequency_mhz * 1'000'000; + }); +} + +optional tscFrequency() { + return tscFrequencyFromCrystalRatio().or_else(tscFrequencyFromProcessorBase); +} + +} // namespace + +optional apic::calibrateTimer(const acpi::Madt&) { + if (!isTscSupported()) { + return {}; + } + return tscFrequency().map(construct()); +} diff --git a/kernel/src/init/init.cpp b/kernel/src/init/init.cpp index 0b2b9ee..2ff7571 100644 --- a/kernel/src/init/init.cpp +++ b/kernel/src/init/init.cpp @@ -1,11 +1,12 @@ #include "init/init.h" -#include "init/acpi.h" -#include "init/apic.h" +#include "acpi/acpi.h" +#include "acpi/madt.h" #include "init/multiboot2.h" #include "init/print.h" #include "memory/allocator.h" #include "memory/pointer.h" #include "memory/sections.h" +#include "util/utility.h" [[maybe_unused]] [[noreturn]] __attribute__((used)) static void halt() { while (true) @@ -18,23 +19,30 @@ } static int step_depth = 0; -template -static void step(const char* name, T&& fn) { + +static void print_step_boundary(const char* prefix, const char* name) { for (int i = 0; i < step_depth; ++i) { print(" "); } - print("Starting "); + print(prefix); print(name); print("\n"); +} + +template +static auto step(const char* name, Fn&& fn) { + print_step_boundary("Starting ", name); ++step_depth; - fn(); - --step_depth; - for (int i = 0; i < step_depth; ++i) { - print(" "); + if constexpr (is_void_v) { + fn(); + --step_depth; + print_step_boundary("Finished ", name); + } else { + auto result = fn(); + --step_depth; + print_step_boundary("Finished ", name); + return result; } - print("Finished "); - print(name); - print("\n"); } template @@ -145,14 +153,9 @@ void PhysicalAllocator::init(const multiboot::tag_mmap* multiboot_info) { } static void setupMemory() { - multiboot::visit_all( - overloaded{ - [](const multiboot::tag_mmap* mem) { - PhysicalAllocator::init(mem); - }, - [](const auto* tag) {} - } - ); + multiboot::visit_all(overloaded{[](const multiboot::tag_mmap* mem) { + PhysicalAllocator::init(mem); + }}); } static paddr_t rsdp_physical_of(const uint8_t* rsdp_payload) { @@ -173,8 +176,7 @@ static acpi::Rsdp findRsdp() { if (!rsdp.has_value()) { rsdp = rsdp_physical_of(tag->rsdp); } - }, - [&](const auto* tag) {} + } } ); @@ -189,18 +191,19 @@ struct AcpiDiscoveryResult { optional madt; }; -static void setupAcpi() { - auto rsdp = findRsdp(); - rsdp.for_each_table(overloaded{[&](const acpi::sdt_header_t* header) { - print(acpi::unfold_signature(header->signature).text); - print("\n"); +static AcpiDiscoveryResult setupAcpi() { + AcpiDiscoveryResult res{}; + res.rsdp = findRsdp(); + res.rsdp->for_each_table(overloaded{[&](const acpi::madt_t* header) { + res.madt = acpi::Madt{header}; }}); + return res; } [[maybe_unused]] [[noreturn]] __attribute__((used)) void init() { initFromLow(); print("Reached init\n"); step("memory", setupMemory); - step("acpi", setupAcpi); + auto acpi = step("acpi", setupAcpi); halt(); }