banan-os/kernel/include/kernel/Processor.h

189 lines
5.0 KiB
C++

#pragma once
#include <BAN/Atomic.h>
#include <BAN/ForwardList.h>
#include <kernel/Arch.h>
#include <kernel/GDT.h>
#include <kernel/IDT.h>
#include <kernel/InterruptStack.h>
#include <kernel/ProcessorID.h>
#include <kernel/Scheduler.h>
namespace Kernel
{
enum class InterruptState
{
Disabled,
Enabled,
};
#if ARCH(x86_64) || ARCH(i686)
class Processor
{
BAN_NON_COPYABLE(Processor);
BAN_NON_MOVABLE(Processor);
public:
struct SMPMessage
{
enum class Type
{
FlushTLB,
NewThread,
UnblockThread,
};
SMPMessage* next { nullptr };
Type type;
union
{
struct
{
uintptr_t vaddr;
size_t page_count;
} flush_tlb;
SchedulerQueue::Node* new_thread;
SchedulerQueue::Node* unblock_thread;
};
};
public:
static Processor& create(ProcessorID id);
static Processor& initialize();
static ProcessorID current_id() { return read_gs_sized<ProcessorID>(offsetof(Processor, m_id)); }
static ProcessorID id_from_index(size_t index);
static uint8_t count() { return s_processor_count; }
static bool is_smp_enabled() { return s_is_smp_enabled; }
static void wait_until_processors_ready();
static void toggle_should_print_cpu_load() { s_should_print_cpu_load = !s_should_print_cpu_load; }
static bool get_should_print_cpu_load() { return s_should_print_cpu_load; }
static ProcessorID bsb_id() { return s_bsb_id; }
static bool current_is_bsb() { return current_id() == bsb_id(); }
static void set_interrupt_state(InterruptState state)
{
if (state == InterruptState::Enabled)
asm volatile("sti");
else
asm volatile("cli");
}
static InterruptState get_interrupt_state()
{
uintptr_t flags;
asm volatile("pushf; pop %0" : "=rm"(flags));
if (flags & (1 << 9))
return InterruptState::Enabled;
return InterruptState::Disabled;
};
static void pause()
{
__builtin_ia32_pause();
if (is_smp_enabled())
handle_smp_messages();
}
static uintptr_t current_stack_bottom() { return read_gs_sized<uintptr_t>(offsetof(Processor, m_stack)); }
static uintptr_t current_stack_top() { return current_stack_bottom() + s_stack_size; }
uintptr_t stack_bottom() const { return reinterpret_cast<uintptr_t>(m_stack); }
uintptr_t stack_top() const { return stack_bottom() + s_stack_size; }
static GDT& gdt() { return *read_gs_sized<GDT*>(offsetof(Processor, m_gdt)); }
static IDT& idt() { return *read_gs_sized<IDT*>(offsetof(Processor, m_idt)); }
static void* get_current_page_table() { return read_gs_sized<void*>(offsetof(Processor, m_current_page_table)); }
static void set_current_page_table(void* page_table) { write_gs_sized<void*>(offsetof(Processor, m_current_page_table), page_table); }
static void yield();
static Scheduler& scheduler() { return *read_gs_sized<Scheduler*>(offsetof(Processor, m_scheduler)); }
static void handle_ipi();
static void handle_smp_messages();
static void send_smp_message(ProcessorID, const SMPMessage&, bool send_ipi = true);
static void broadcast_smp_message(const SMPMessage&);
private:
Processor() = default;
~Processor() { ASSERT_NOT_REACHED(); }
static ProcessorID read_processor_id();
template<typename T>
static T read_gs_sized(uintptr_t offset) requires(sizeof(T) <= 8)
{
#define __ASM_INPUT(operation) asm volatile(operation " %%gs:%a[offset], %[result]" : [result]"=r"(result) : [offset]"ir"(offset))
T result;
if constexpr(sizeof(T) == 8)
__ASM_INPUT("movq");
if constexpr(sizeof(T) == 4)
__ASM_INPUT("movl");
if constexpr(sizeof(T) == 2)
__ASM_INPUT("movw");
if constexpr(sizeof(T) == 1)
__ASM_INPUT("movb");
return result;
#undef __ASM_INPUT
}
template<typename T>
static void write_gs_sized(uintptr_t offset, T value) requires(sizeof(T) <= 8)
{
#define __ASM_INPUT(operation) asm volatile(operation " %[value], %%gs:%a[offset]" :: [value]"r"(value), [offset]"ir"(offset) : "memory")
if constexpr(sizeof(T) == 8)
__ASM_INPUT("movq");
if constexpr(sizeof(T) == 4)
__ASM_INPUT("movl");
if constexpr(sizeof(T) == 2)
__ASM_INPUT("movw");
if constexpr(sizeof(T) == 1)
__ASM_INPUT("movb");
#undef __ASM_INPUT
}
private:
static ProcessorID s_bsb_id;
static BAN::Atomic<uint8_t> s_processor_count;
static BAN::Atomic<bool> s_is_smp_enabled;
static BAN::Atomic<bool> s_should_print_cpu_load;
ProcessorID m_id { PROCESSOR_NONE };
static constexpr size_t s_stack_size { 4096 };
void* m_stack { nullptr };
GDT* m_gdt { nullptr };
IDT* m_idt { nullptr };
Scheduler* m_scheduler { nullptr };
uint64_t m_start_ns { 0 };
uint64_t m_idle_ns { 0 };
uint64_t m_last_update_ns { 0 };
uint64_t m_next_update_ns { 0 };
BAN::Atomic<bool> m_smp_pending_lock { false };
SMPMessage* m_smp_pending { nullptr };
BAN::Atomic<bool> m_smp_free_lock { false };
SMPMessage* m_smp_free { nullptr };
SMPMessage* m_smp_message_storage;
void* m_current_page_table { nullptr };
friend class BAN::Array<Processor, 0xFF>;
};
#else
#error
#endif
}