diff --git a/Kernel/Arch/x86/CPUID.h b/Kernel/Arch/x86/CPUID.h index 29eecd28d4..7b214eee0c 100644 --- a/Kernel/Arch/x86/CPUID.h +++ b/Kernel/Arch/x86/CPUID.h @@ -58,6 +58,7 @@ enum class CPUFeature : u32 { FXSR = (1 << 23), LM = (1 << 24), HYPERVISOR = (1 << 25), + PAT = (1 << 26), }; } diff --git a/Kernel/Arch/x86/PageDirectory.h b/Kernel/Arch/x86/PageDirectory.h index f0db3a22e6..d0e241e987 100644 --- a/Kernel/Arch/x86/PageDirectory.h +++ b/Kernel/Arch/x86/PageDirectory.h @@ -95,6 +95,7 @@ public: UserSupervisor = 1 << 2, WriteThrough = 1 << 3, CacheDisabled = 1 << 4, + PAT = 1 << 7, Global = 1 << 8, NoExecute = 0x8000000000000000ULL, }; @@ -120,6 +121,9 @@ public: bool is_execute_disabled() const { return (raw() & NoExecute) == NoExecute; } void set_execute_disabled(bool b) { set_bit(NoExecute, b); } + bool is_pat() const { return (raw() & PAT) == PAT; } + void set_pat(bool b) { set_bit(PAT, b); } + bool is_null() const { return m_raw == 0; } void clear() { m_raw = 0; } diff --git a/Kernel/Arch/x86/Processor.h b/Kernel/Arch/x86/Processor.h index 919ded724c..7b9d430e48 100644 --- a/Kernel/Arch/x86/Processor.h +++ b/Kernel/Arch/x86/Processor.h @@ -39,6 +39,7 @@ struct ProcessorMessageEntry; # define MSR_GS_BASE 0xc0000101 #endif #define MSR_IA32_EFER 0xc0000080 +#define MSR_IA32_PAT 0x277 // FIXME: Find a better place for these extern "C" void thread_context_first_enter(void); diff --git a/Kernel/Arch/x86/common/Processor.cpp b/Kernel/Arch/x86/common/Processor.cpp index 9c61facd65..aeb8cd3d44 100644 --- a/Kernel/Arch/x86/common/Processor.cpp +++ b/Kernel/Arch/x86/common/Processor.cpp @@ -114,6 +114,8 @@ UNMAP_AFTER_INIT void Processor::cpu_detect() if ((family == 6 && model >= 3) || (family == 0xf && model >= 0xe)) set_feature(CPUFeature::CONSTANT_TSC); } + if (processor_info.edx() & (1 << 16)) + set_feature(CPUFeature::PAT); u32 max_extended_leaf = CPUID(0x80000000).eax(); @@ -190,6 +192,18 @@ UNMAP_AFTER_INIT void Processor::cpu_setup() ia32_efer.set(ia32_efer.get() | 0x800); } + if (has_feature(CPUFeature::PAT)) { + MSR ia32_pat(MSR_IA32_PAT); + // Set PA4 to Write Comine. This allows us to + // use this mode by only setting the bit in the PTE + // and leaving all other bits in the upper levels unset, + // which maps to setting bit 3 of the index, resulting + // in the index value 0 or 4. + u64 pat = ia32_pat.get() & ~(0x7ull << 32); + pat |= 0x1ull << 32; // set WC mode for PA4 + ia32_pat.set(pat); + } + if (has_feature(CPUFeature::SMEP)) { // Turn on CR4.SMEP write_cr4(read_cr4() | 0x100000); @@ -309,6 +323,8 @@ NonnullOwnPtr Processor::features_string() const return "hypervisor"sv; // no default statement here intentionally so that we get // a warning if a new feature is forgotten to be added here + case CPUFeature::PAT: + return "pat"sv; } // Shouldn't ever happen return "???"sv; diff --git a/Kernel/Memory/Region.cpp b/Kernel/Memory/Region.cpp index b7c741ec19..d3323fbc99 100644 --- a/Kernel/Memory/Region.cpp +++ b/Kernel/Memory/Region.cpp @@ -212,6 +212,8 @@ bool Region::map_individual_page_impl(size_t page_index) pte->set_writable(is_writable()); if (Processor::current().has_feature(CPUFeature::NX)) pte->set_execute_disabled(!is_executable()); + if (Processor::current().has_feature(CPUFeature::PAT)) + pte->set_pat(is_write_combine()); pte->set_user_allowed(user_allowed); } return true; @@ -311,6 +313,18 @@ void Region::remap() TODO(); } +ErrorOr Region::set_write_combine(bool enable) +{ + if (enable && !Processor::current().has_feature(CPUFeature::PAT)) { + dbgln("PAT is not supported, implement MTRR fallback if available"); + return Error::from_errno(ENOTSUP); + } + + m_write_combine = enable; + remap(); + return {}; +} + void Region::clear_to_zero() { VERIFY(vmobject().is_anonymous()); diff --git a/Kernel/Memory/Region.h b/Kernel/Memory/Region.h index 3c447296e7..7dc83a78cb 100644 --- a/Kernel/Memory/Region.h +++ b/Kernel/Memory/Region.h @@ -87,6 +87,9 @@ public: [[nodiscard]] bool is_mmap() const { return m_mmap; } void set_mmap(bool mmap) { m_mmap = mmap; } + [[nodiscard]] bool is_write_combine() const { return m_write_combine; } + ErrorOr set_write_combine(bool); + [[nodiscard]] bool is_user() const { return !is_kernel(); } [[nodiscard]] bool is_kernel() const { return vaddr().get() < USER_RANGE_BASE || vaddr().get() >= kernel_mapping_base; } @@ -220,6 +223,7 @@ private: bool m_stack : 1 { false }; bool m_mmap : 1 { false }; bool m_syscall_region : 1 { false }; + bool m_write_combine : 1 { false }; IntrusiveRedBlackTreeNode> m_tree_node; IntrusiveListNode m_vmobject_list_node;