diff options
Diffstat (limited to 'Kernel/Arch/x86/CPU.h')
-rw-r--r-- | Kernel/Arch/x86/CPU.h | 1063 |
1 files changed, 1063 insertions, 0 deletions
diff --git a/Kernel/Arch/x86/CPU.h b/Kernel/Arch/x86/CPU.h new file mode 100644 index 0000000000..34c5b43fa3 --- /dev/null +++ b/Kernel/Arch/x86/CPU.h @@ -0,0 +1,1063 @@ +/* + * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/Atomic.h> +#include <AK/Badge.h> +#include <AK/Noncopyable.h> +#include <AK/Vector.h> + +#include <Kernel/Arch/x86/DescriptorTable.h> +#include <Kernel/Arch/x86/TSS.h> + +#include <Kernel/PhysicalAddress.h> +#include <Kernel/VirtualAddress.h> +#include <LibC/sys/arch/i386/regs.h> + +#define READONLY_AFTER_INIT __attribute__((section(".ro_after_init"))) +#define UNMAP_AFTER_INIT NEVER_INLINE __attribute__((section(".unmap_after_init"))) + +#define PAGE_SIZE 4096 +#define GENERIC_INTERRUPT_HANDLERS_COUNT (256 - IRQ_VECTOR_BASE) +#define PAGE_MASK ((FlatPtr)0xfffff000u) + +namespace Kernel { + +class MemoryManager; +class PageDirectory; +class PageTableEntry; + +static constexpr u32 safe_eflags_mask = 0xdff; +static constexpr u32 iopl_mask = 3u << 12; + +inline u32 get_iopl_from_eflags(u32 eflags) +{ + return (eflags & iopl_mask) >> 12; +} + +class PageDirectoryEntry { +public: + const PageTableEntry* page_table_base() const { return reinterpret_cast<PageTableEntry*>(m_raw & 0xfffff000u); } + PageTableEntry* page_table_base() { return reinterpret_cast<PageTableEntry*>(m_raw & 0xfffff000u); } + void set_page_table_base(u32 value) + { + m_raw &= 0x8000000000000fffULL; + m_raw |= value & 0xfffff000; + } + + bool is_null() const { return m_raw == 0; } + void clear() { m_raw = 0; } + + u64 raw() const { return m_raw; } + void copy_from(Badge<PageDirectory>, const PageDirectoryEntry& other) { m_raw = other.m_raw; } + + enum Flags { + Present = 1 << 0, + ReadWrite = 1 << 1, + UserSupervisor = 1 << 2, + WriteThrough = 1 << 3, + CacheDisabled = 1 << 4, + Huge = 1 << 7, + Global = 1 << 8, + NoExecute = 0x8000000000000000ULL, + }; + + bool is_present() const { return raw() & Present; } + void set_present(bool b) { set_bit(Present, b); } + + bool is_user_allowed() const { return raw() & UserSupervisor; } + void set_user_allowed(bool b) { set_bit(UserSupervisor, b); } + + bool is_huge() const { return raw() & Huge; } + void set_huge(bool b) { set_bit(Huge, b); } + + bool is_writable() const { return raw() & ReadWrite; } + void set_writable(bool b) { set_bit(ReadWrite, b); } + + bool is_write_through() const { return raw() & WriteThrough; } + void set_write_through(bool b) { set_bit(WriteThrough, b); } + + bool is_cache_disabled() const { return raw() & CacheDisabled; } + void set_cache_disabled(bool b) { set_bit(CacheDisabled, b); } + + bool is_global() const { return raw() & Global; } + void set_global(bool b) { set_bit(Global, b); } + + bool is_execute_disabled() const { return raw() & NoExecute; } + void set_execute_disabled(bool b) { set_bit(NoExecute, b); } + + void set_bit(u64 bit, bool value) + { + if (value) + m_raw |= bit; + else + m_raw &= ~bit; + } + +private: + u64 m_raw; +}; + +class PageTableEntry { +public: + void* physical_page_base() { return reinterpret_cast<void*>(m_raw & 0xfffff000u); } + void set_physical_page_base(u32 value) + { + m_raw &= 0x8000000000000fffULL; + m_raw |= value & 0xfffff000; + } + + u64 raw() const { return (u32)m_raw; } + + enum Flags { + Present = 1 << 0, + ReadWrite = 1 << 1, + UserSupervisor = 1 << 2, + WriteThrough = 1 << 3, + CacheDisabled = 1 << 4, + Global = 1 << 8, + NoExecute = 0x8000000000000000ULL, + }; + + bool is_present() const { return raw() & Present; } + void set_present(bool b) { set_bit(Present, b); } + + bool is_user_allowed() const { return raw() & UserSupervisor; } + void set_user_allowed(bool b) { set_bit(UserSupervisor, b); } + + bool is_writable() const { return raw() & ReadWrite; } + void set_writable(bool b) { set_bit(ReadWrite, b); } + + bool is_write_through() const { return raw() & WriteThrough; } + void set_write_through(bool b) { set_bit(WriteThrough, b); } + + bool is_cache_disabled() const { return raw() & CacheDisabled; } + void set_cache_disabled(bool b) { set_bit(CacheDisabled, b); } + + bool is_global() const { return raw() & Global; } + void set_global(bool b) { set_bit(Global, b); } + + bool is_execute_disabled() const { return raw() & NoExecute; } + void set_execute_disabled(bool b) { set_bit(NoExecute, b); } + + bool is_null() const { return m_raw == 0; } + void clear() { m_raw = 0; } + + void set_bit(u64 bit, bool value) + { + if (value) + m_raw |= bit; + else + m_raw &= ~bit; + } + +private: + u64 m_raw; +}; + +static_assert(sizeof(PageDirectoryEntry) == 8); +static_assert(sizeof(PageTableEntry) == 8); + +class PageDirectoryPointerTable { +public: + PageDirectoryEntry* directory(size_t index) + { + return (PageDirectoryEntry*)(raw[index] & ~0xfffu); + } + + u64 raw[4]; +}; + +class GenericInterruptHandler; +struct RegisterState; + +const DescriptorTablePointer& get_gdtr(); +const DescriptorTablePointer& get_idtr(); +void register_interrupt_handler(u8 number, void (*handler)()); +void register_user_callable_interrupt_handler(u8 number, void (*handler)()); +GenericInterruptHandler& get_interrupt_handler(u8 interrupt_number); +void register_generic_interrupt_handler(u8 number, GenericInterruptHandler&); +void unregister_generic_interrupt_handler(u8 number, GenericInterruptHandler&); +void flush_idt(); +void load_task_register(u16 selector); +[[noreturn]] void handle_crash(RegisterState&, const char* description, int signal, bool out_of_memory = false); + +#define LSW(x) ((u32)(x)&0xFFFF) +#define MSW(x) (((u32)(x) >> 16) & 0xFFFF) +#define LSB(x) ((x)&0xFF) +#define MSB(x) (((x) >> 8) & 0xFF) + +#define cli() asm volatile("cli" :: \ + : "memory") +#define sti() asm volatile("sti" :: \ + : "memory") + +inline FlatPtr cpu_flags() +{ + FlatPtr flags; + asm volatile( + "pushf\n" + "pop %0\n" + : "=rm"(flags)::"memory"); + return flags; +} + +inline void set_fs(u16 segment) +{ + asm volatile( + "mov %%ax, %%fs" ::"a"(segment) + : "memory"); +} + +inline void set_gs(u16 segment) +{ + asm volatile( + "mov %%ax, %%gs" ::"a"(segment) + : "memory"); +} + +inline u16 get_fs() +{ + u16 fs; + asm("mov %%fs, %%eax" + : "=a"(fs)); + return fs; +} + +inline u16 get_gs() +{ + u16 gs; + asm("mov %%gs, %%eax" + : "=a"(gs)); + return gs; +} + +inline u32 read_fs_u32(u32 offset) +{ + u32 val; + asm volatile( + "movl %%fs:%a[off], %k[val]" + : [val] "=r"(val) + : [off] "ir"(offset)); + return val; +} + +inline FlatPtr read_fs_ptr(u32 offset) +{ + return read_fs_u32(offset); +} + +inline void write_fs_u32(u32 offset, u32 val) +{ + asm volatile( + "movl %k[val], %%fs:%a[off]" ::[off] "ir"(offset), [val] "ir"(val) + : "memory"); +} + +inline bool are_interrupts_enabled() +{ + return cpu_flags() & 0x200; +} + +class InterruptDisabler { +public: + InterruptDisabler() + { + m_flags = cpu_flags(); + cli(); + } + + ~InterruptDisabler() + { + if (m_flags & 0x200) + sti(); + } + +private: + u32 m_flags; +}; + +class NonMaskableInterruptDisabler { +public: + NonMaskableInterruptDisabler(); + ~NonMaskableInterruptDisabler(); +}; + +/* Map IRQ0-15 @ ISR 0x50-0x5F */ +#define IRQ_VECTOR_BASE 0x50 + +struct PageFaultFlags { + enum Flags { + NotPresent = 0x00, + ProtectionViolation = 0x01, + Read = 0x00, + Write = 0x02, + UserMode = 0x04, + SupervisorMode = 0x00, + ReservedBitViolation = 0x08, + InstructionFetch = 0x10, + }; +}; + +class PageFault { +public: + PageFault(u16 code, VirtualAddress vaddr) + : m_code(code) + , m_vaddr(vaddr) + { + } + + enum class Type { + PageNotPresent = PageFaultFlags::NotPresent, + ProtectionViolation = PageFaultFlags::ProtectionViolation, + }; + + enum class Access { + Read = PageFaultFlags::Read, + Write = PageFaultFlags::Write, + }; + + VirtualAddress vaddr() const { return m_vaddr; } + u16 code() const { return m_code; } + + Type type() const { return (Type)(m_code & 1); } + Access access() const { return (Access)(m_code & 2); } + + bool is_not_present() const { return (m_code & 1) == PageFaultFlags::NotPresent; } + bool is_protection_violation() const { return (m_code & 1) == PageFaultFlags::ProtectionViolation; } + bool is_read() const { return (m_code & 2) == PageFaultFlags::Read; } + bool is_write() const { return (m_code & 2) == PageFaultFlags::Write; } + bool is_user() const { return (m_code & 4) == PageFaultFlags::UserMode; } + bool is_supervisor() const { return (m_code & 4) == PageFaultFlags::SupervisorMode; } + bool is_instruction_fetch() const { return (m_code & 16) == PageFaultFlags::InstructionFetch; } + +private: + u16 m_code; + VirtualAddress m_vaddr; +}; + +struct [[gnu::packed]] RegisterState { + FlatPtr ss; + FlatPtr gs; + FlatPtr fs; + FlatPtr es; + FlatPtr ds; + FlatPtr edi; + FlatPtr esi; + FlatPtr ebp; + FlatPtr esp; + FlatPtr ebx; + FlatPtr edx; + FlatPtr ecx; + FlatPtr eax; + u16 exception_code; + u16 isr_number; +#if ARCH(X86_64) + u32 padding; +#endif + FlatPtr eip; + FlatPtr cs; + FlatPtr eflags; + FlatPtr userspace_esp; + FlatPtr userspace_ss; +}; + +#if ARCH(I386) +# define REGISTER_STATE_SIZE (19 * 4) +#else +# define REGISTER_STATE_SIZE (19 * 8) +#endif +static_assert(REGISTER_STATE_SIZE == sizeof(RegisterState)); + +void copy_kernel_registers_into_ptrace_registers(PtraceRegisters&, const RegisterState&); +void copy_ptrace_registers_into_kernel_registers(RegisterState&, const PtraceRegisters&); + +struct [[gnu::aligned(16)]] FPUState +{ + u8 buffer[512]; +}; + +constexpr FlatPtr page_base_of(FlatPtr address) +{ + return address & PAGE_MASK; +} + +inline FlatPtr page_base_of(const void* address) +{ + return page_base_of((FlatPtr)address); +} + +constexpr FlatPtr offset_in_page(FlatPtr address) +{ + return address & (~PAGE_MASK); +} + +inline FlatPtr offset_in_page(const void* address) +{ + return offset_in_page((FlatPtr)address); +} + +FlatPtr read_cr0(); +FlatPtr read_cr2(); +FlatPtr read_cr3(); +FlatPtr read_cr4(); +u64 read_xcr0(); + +void write_cr0(FlatPtr); +void write_cr3(FlatPtr); +void write_cr4(FlatPtr); +void write_xcr0(u64); + +FlatPtr read_dr6(); + +static inline bool is_kernel_mode() +{ + u16 cs; + asm volatile( + "mov %%cs, %[cs] \n" + : [cs] "=g"(cs)); + return (cs & 3) == 0; +} + +class CPUID { +public: + explicit CPUID(u32 function) { asm volatile("cpuid" + : "=a"(m_eax), "=b"(m_ebx), "=c"(m_ecx), "=d"(m_edx) + : "a"(function), "c"(0)); } + u32 eax() const { return m_eax; } + u32 ebx() const { return m_ebx; } + u32 ecx() const { return m_ecx; } + u32 edx() const { return m_edx; } + +private: + u32 m_eax { 0xffffffff }; + u32 m_ebx { 0xffffffff }; + u32 m_ecx { 0xffffffff }; + u32 m_edx { 0xffffffff }; +}; + +inline void read_tsc(u32& lsw, u32& msw) +{ + asm volatile("rdtsc" + : "=d"(msw), "=a"(lsw)); +} + +inline u64 read_tsc() +{ + u32 lsw; + u32 msw; + read_tsc(lsw, msw); + return ((u64)msw << 32) | lsw; +} + +// FIXME: This can't hold every CPU feature as-is. +enum class CPUFeature : u32 { + NX = (1 << 0), + PAE = (1 << 1), + PGE = (1 << 2), + RDRAND = (1 << 3), + RDSEED = (1 << 4), + SMAP = (1 << 5), + SMEP = (1 << 6), + SSE = (1 << 7), + TSC = (1 << 8), + RDTSCP = (1 << 9), + CONSTANT_TSC = (1 << 10), + NONSTOP_TSC = (1 << 11), + UMIP = (1 << 12), + SEP = (1 << 13), + SYSCALL = (1 << 14), + MMX = (1 << 15), + SSE2 = (1 << 16), + SSE3 = (1 << 17), + SSSE3 = (1 << 18), + SSE4_1 = (1 << 19), + SSE4_2 = (1 << 20), + XSAVE = (1 << 21), + AVX = (1 << 22), +}; + +class Thread; +struct TrapFrame; + +#define GDT_SELECTOR_CODE0 0x08 +#define GDT_SELECTOR_DATA0 0x10 +#define GDT_SELECTOR_CODE3 0x18 +#define GDT_SELECTOR_DATA3 0x20 +#define GDT_SELECTOR_TLS 0x28 +#define GDT_SELECTOR_PROC 0x30 +#define GDT_SELECTOR_TSS 0x38 + +// SYSENTER makes certain assumptions on how the GDT is structured: +static_assert(GDT_SELECTOR_CODE0 + 8 == GDT_SELECTOR_DATA0); // SS0 = CS0 + 8 + +// SYSEXIT makes certain assumptions on how the GDT is structured: +static_assert(GDT_SELECTOR_CODE0 + 16 == GDT_SELECTOR_CODE3); // CS3 = CS0 + 16 +static_assert(GDT_SELECTOR_CODE0 + 24 == GDT_SELECTOR_DATA3); // SS3 = CS0 + 32 + +class ProcessorInfo; +class SchedulerPerProcessorData; +struct MemoryManagerData; +struct ProcessorMessageEntry; + +struct ProcessorMessage { + enum Type { + FlushTlb, + Callback, + CallbackWithData + }; + Type type; + volatile u32 refs; // atomic + union { + ProcessorMessage* next; // only valid while in the pool + struct { + void (*handler)(); + } callback; + struct { + void* data; + void (*handler)(void*); + void (*free)(void*); + } callback_with_data; + struct { + const PageDirectory* page_directory; + u8* ptr; + size_t page_count; + } flush_tlb; + }; + + volatile bool async; + + ProcessorMessageEntry* per_proc_entries; +}; + +struct ProcessorMessageEntry { + ProcessorMessageEntry* next; + ProcessorMessage* msg; +}; + +struct DeferredCallEntry { + DeferredCallEntry* next; + union { + struct { + void (*handler)(); + } callback; + struct { + void* data; + void (*handler)(void*); + void (*free)(void*); + } callback_with_data; + }; + bool have_data; + bool was_allocated; +}; + +class Processor { + friend class ProcessorInfo; + + AK_MAKE_NONCOPYABLE(Processor); + AK_MAKE_NONMOVABLE(Processor); + + Processor* m_self; + + DescriptorTablePointer m_gdtr; + Descriptor m_gdt[256]; + u32 m_gdt_length; + + u32 m_cpu; + u32 m_in_irq; + Atomic<u32, AK::MemoryOrder::memory_order_relaxed> m_in_critical; + static Atomic<u32> s_idle_cpu_mask; + + TSS m_tss; + static FPUState s_clean_fpu_state; + CPUFeature m_features; + static volatile u32 g_total_processors; // atomic + u8 m_physical_address_bit_width; + + ProcessorInfo* m_info; + MemoryManagerData* m_mm_data; + SchedulerPerProcessorData* m_scheduler_data; + Thread* m_current_thread; + Thread* m_idle_thread; + + volatile ProcessorMessageEntry* m_message_queue; // atomic, LIFO + + bool m_invoke_scheduler_async; + bool m_scheduler_initialized; + Atomic<bool> m_halt_requested; + + DeferredCallEntry* m_pending_deferred_calls; // in reverse order + DeferredCallEntry* m_free_deferred_call_pool_entry; + DeferredCallEntry m_deferred_call_pool[5]; + + void gdt_init(); + void write_raw_gdt_entry(u16 selector, u32 low, u32 high); + void write_gdt_entry(u16 selector, Descriptor& descriptor); + static Vector<Processor*>& processors(); + + static void smp_return_to_pool(ProcessorMessage& msg); + static ProcessorMessage& smp_get_from_pool(); + static void smp_cleanup_message(ProcessorMessage& msg); + bool smp_queue_message(ProcessorMessage& msg); + static void smp_unicast_message(u32 cpu, ProcessorMessage& msg, bool async); + static void smp_broadcast_message(ProcessorMessage& msg); + static void smp_broadcast_wait_sync(ProcessorMessage& msg); + static void smp_broadcast_halt(); + + void deferred_call_pool_init(); + void deferred_call_execute_pending(); + DeferredCallEntry* deferred_call_get_free(); + void deferred_call_return_to_pool(DeferredCallEntry*); + void deferred_call_queue_entry(DeferredCallEntry*); + + void cpu_detect(); + void cpu_setup(); + + String features_string() const; + +public: + Processor() = default; + + void early_initialize(u32 cpu); + void initialize(u32 cpu); + + void idle_begin() + { + s_idle_cpu_mask.fetch_or(1u << m_cpu, AK::MemoryOrder::memory_order_relaxed); + } + + void idle_end() + { + s_idle_cpu_mask.fetch_and(~(1u << m_cpu), AK::MemoryOrder::memory_order_relaxed); + } + + static u32 count() + { + // NOTE: because this value never changes once all APs are booted, + // we don't really need to do an atomic_load() on this variable + return g_total_processors; + } + + ALWAYS_INLINE static void wait_check() + { + Processor::current().smp_process_pending_messages(); + // TODO: pause + } + + [[noreturn]] static void halt(); + + static void flush_entire_tlb_local() + { + write_cr3(read_cr3()); + } + + static void flush_tlb_local(VirtualAddress vaddr, size_t page_count); + static void flush_tlb(const PageDirectory*, VirtualAddress, size_t); + + Descriptor& get_gdt_entry(u16 selector); + void flush_gdt(); + const DescriptorTablePointer& get_gdtr(); + + static Processor& by_id(u32 cpu); + + static size_t processor_count() { return processors().size(); } + + template<typename Callback> + static inline IterationDecision for_each(Callback callback) + { + auto& procs = processors(); + size_t count = procs.size(); + for (size_t i = 0; i < count; i++) { + if (callback(*procs[i]) == IterationDecision::Break) + return IterationDecision::Break; + } + return IterationDecision::Continue; + } + + ALWAYS_INLINE u8 physical_address_bit_width() const { return m_physical_address_bit_width; } + + ALWAYS_INLINE ProcessorInfo& info() { return *m_info; } + + ALWAYS_INLINE static Processor& current() + { + return *(Processor*)read_fs_ptr(__builtin_offsetof(Processor, m_self)); + } + + ALWAYS_INLINE static bool is_initialized() + { + return get_fs() == GDT_SELECTOR_PROC && read_fs_u32(__builtin_offsetof(Processor, m_self)) != 0; + } + + ALWAYS_INLINE void set_scheduler_data(SchedulerPerProcessorData& scheduler_data) + { + m_scheduler_data = &scheduler_data; + } + + ALWAYS_INLINE SchedulerPerProcessorData& get_scheduler_data() const + { + return *m_scheduler_data; + } + + ALWAYS_INLINE void set_mm_data(MemoryManagerData& mm_data) + { + m_mm_data = &mm_data; + } + + ALWAYS_INLINE MemoryManagerData& get_mm_data() const + { + return *m_mm_data; + } + + ALWAYS_INLINE Thread* idle_thread() const + { + return m_idle_thread; + } + + ALWAYS_INLINE void set_idle_thread(Thread& idle_thread) + { + m_idle_thread = &idle_thread; + } + + ALWAYS_INLINE static Thread* current_thread() + { + // If we were to use Processor::current here, we'd have to + // disable interrupts to prevent a race where we may get pre-empted + // right after getting the Processor structure and then get moved + // to another processor, which would lead us to get the wrong thread. + // To avoid having to disable interrupts, we can just read the field + // directly in an atomic fashion, similar to Processor::current. + return (Thread*)read_fs_ptr(__builtin_offsetof(Processor, m_current_thread)); + } + + ALWAYS_INLINE static void set_current_thread(Thread& current_thread) + { + // See comment in Processor::current_thread + write_fs_u32(__builtin_offsetof(Processor, m_current_thread), FlatPtr(¤t_thread)); + } + + ALWAYS_INLINE u32 get_id() const + { + // NOTE: This variant should only be used when iterating over all + // Processor instances, or when it's guaranteed that the thread + // cannot move to another processor in between calling Processor::current + // and Processor::get_id, or if this fact is not important. + // All other cases should use Processor::id instead! + return m_cpu; + } + + ALWAYS_INLINE static u32 id() + { + // See comment in Processor::current_thread + return read_fs_ptr(__builtin_offsetof(Processor, m_cpu)); + } + + ALWAYS_INLINE u32 raise_irq() + { + return m_in_irq++; + } + + ALWAYS_INLINE void restore_irq(u32 prev_irq) + { + VERIFY(prev_irq <= m_in_irq); + if (!prev_irq) { + u32 prev_critical = 0; + if (m_in_critical.compare_exchange_strong(prev_critical, 1)) { + m_in_irq = prev_irq; + deferred_call_execute_pending(); + auto prev_raised = m_in_critical.exchange(prev_critical); + VERIFY(prev_raised == prev_critical + 1); + check_invoke_scheduler(); + } else if (prev_critical == 0) { + check_invoke_scheduler(); + } + } else { + m_in_irq = prev_irq; + } + } + + ALWAYS_INLINE u32& in_irq() + { + return m_in_irq; + } + + ALWAYS_INLINE void restore_in_critical(u32 critical) + { + m_in_critical = critical; + } + + ALWAYS_INLINE void enter_critical(u32& prev_flags) + { + prev_flags = cpu_flags(); + cli(); + m_in_critical++; + } + + ALWAYS_INLINE void leave_critical(u32 prev_flags) + { + cli(); // Need to prevent IRQs from interrupting us here! + VERIFY(m_in_critical > 0); + if (m_in_critical == 1) { + if (!m_in_irq) { + deferred_call_execute_pending(); + VERIFY(m_in_critical == 1); + } + m_in_critical--; + if (!m_in_irq) + check_invoke_scheduler(); + } else { + m_in_critical--; + } + if (prev_flags & 0x200) + sti(); + else + cli(); + } + + ALWAYS_INLINE u32 clear_critical(u32& prev_flags, bool enable_interrupts) + { + prev_flags = cpu_flags(); + u32 prev_crit = m_in_critical.exchange(0, AK::MemoryOrder::memory_order_acquire); + if (!m_in_irq) + check_invoke_scheduler(); + if (enable_interrupts) + sti(); + return prev_crit; + } + + ALWAYS_INLINE void restore_critical(u32 prev_crit, u32 prev_flags) + { + m_in_critical.store(prev_crit, AK::MemoryOrder::memory_order_release); + VERIFY(!prev_crit || !(prev_flags & 0x200)); + if (prev_flags & 0x200) + sti(); + else + cli(); + } + + ALWAYS_INLINE u32 in_critical() { return m_in_critical.load(); } + + ALWAYS_INLINE const FPUState& clean_fpu_state() const + { + return s_clean_fpu_state; + } + + static void smp_enable(); + bool smp_process_pending_messages(); + + template<typename Callback> + static void smp_broadcast(Callback callback, bool async) + { + auto* data = new Callback(move(callback)); + smp_broadcast( + [](void* data) { + (*reinterpret_cast<Callback*>(data))(); + }, + data, + [](void* data) { + delete reinterpret_cast<Callback*>(data); + }, + async); + } + static void smp_broadcast(void (*callback)(), bool async); + static void smp_broadcast(void (*callback)(void*), void* data, void (*free_data)(void*), bool async); + template<typename Callback> + static void smp_unicast(u32 cpu, Callback callback, bool async) + { + auto* data = new Callback(move(callback)); + smp_unicast( + cpu, + [](void* data) { + (*reinterpret_cast<Callback*>(data))(); + }, + data, + [](void* data) { + delete reinterpret_cast<Callback*>(data); + }, + async); + } + static void smp_unicast(u32 cpu, void (*callback)(), bool async); + static void smp_unicast(u32 cpu, void (*callback)(void*), void* data, void (*free_data)(void*), bool async); + static void smp_broadcast_flush_tlb(const PageDirectory*, VirtualAddress, size_t); + static u32 smp_wake_n_idle_processors(u32 wake_count); + + template<typename Callback> + static void deferred_call_queue(Callback callback) + { + auto* data = new Callback(move(callback)); + deferred_call_queue( + [](void* data) { + (*reinterpret_cast<Callback*>(data))(); + }, + data, + [](void* data) { + delete reinterpret_cast<Callback*>(data); + }); + } + static void deferred_call_queue(void (*callback)()); + static void deferred_call_queue(void (*callback)(void*), void* data, void (*free_data)(void*)); + + ALWAYS_INLINE bool has_feature(CPUFeature f) const + { + return (static_cast<u32>(m_features) & static_cast<u32>(f)) != 0; + } + + void check_invoke_scheduler(); + void invoke_scheduler_async() { m_invoke_scheduler_async = true; } + + void enter_trap(TrapFrame& trap, bool raise_irq); + + void exit_trap(TrapFrame& trap); + + [[noreturn]] void initialize_context_switching(Thread& initial_thread); + void switch_context(Thread*& from_thread, Thread*& to_thread); + [[noreturn]] static void assume_context(Thread& thread, FlatPtr flags); + u32 init_context(Thread& thread, bool leave_crit); + static Vector<FlatPtr> capture_stack_trace(Thread& thread, size_t max_frames = 0); + + String platform_string() const; +}; + +class ScopedCritical { + AK_MAKE_NONCOPYABLE(ScopedCritical); + +public: + ScopedCritical() + { + enter(); + } + + ~ScopedCritical() + { + if (m_valid) + leave(); + } + + ScopedCritical(ScopedCritical&& from) + : m_prev_flags(exchange(from.m_prev_flags, 0)) + , m_valid(exchange(from.m_valid, false)) + { + } + + ScopedCritical& operator=(ScopedCritical&& from) + { + if (&from != this) { + m_prev_flags = exchange(from.m_prev_flags, 0); + m_valid = exchange(from.m_valid, false); + } + return *this; + } + + void leave() + { + VERIFY(m_valid); + m_valid = false; + Processor::current().leave_critical(m_prev_flags); + } + + void enter() + { + VERIFY(!m_valid); + m_valid = true; + Processor::current().enter_critical(m_prev_flags); + } + +private: + u32 m_prev_flags { 0 }; + bool m_valid { false }; +}; + +struct TrapFrame { + u32 prev_irq_level; + TrapFrame* next_trap; + RegisterState* regs; // must be last + + TrapFrame() = delete; + TrapFrame(const TrapFrame&) = delete; + TrapFrame(TrapFrame&&) = delete; + TrapFrame& operator=(const TrapFrame&) = delete; + TrapFrame& operator=(TrapFrame&&) = delete; +}; + +#if ARCH(I386) +# define TRAP_FRAME_SIZE (3 * 4) +#else +# define TRAP_FRAME_SIZE (3 * 8) +#endif + +static_assert(TRAP_FRAME_SIZE == sizeof(TrapFrame)); + +extern "C" void enter_trap_no_irq(TrapFrame*); +extern "C" void enter_trap(TrapFrame*); +extern "C" void exit_trap(TrapFrame*); + +class MSR { + uint32_t m_msr; + +public: + static bool have() + { + CPUID id(1); + return (id.edx() & (1 << 5)) != 0; + } + + MSR(const MSR&) = delete; + MSR& operator=(const MSR&) = delete; + + MSR(uint32_t msr) + : m_msr(msr) + { + } + + void get(u32& low, u32& high) + { + asm volatile("rdmsr" + : "=a"(low), "=d"(high) + : "c"(m_msr)); + } + + void set(u32 low, u32 high) + { + asm volatile("wrmsr" ::"a"(low), "d"(high), "c"(m_msr)); + } +}; + +ALWAYS_INLINE void stac() +{ + if (!Processor::current().has_feature(CPUFeature::SMAP)) + return; + asm volatile("stac" :: + : "cc"); +} + +ALWAYS_INLINE void clac() +{ + if (!Processor::current().has_feature(CPUFeature::SMAP)) + return; + asm volatile("clac" :: + : "cc"); +} +} |