1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 02:47:34 +00:00

Kernel: Implement Page Attribute Table (PAT) support and Write-Combine

This allows us to enable Write-Combine on e.g. framebuffers,
significantly improving performance on bare metal.

To keep things simple we right now only use one of up to three bits
(bit 7 in the PTE), which maps to the PA4 entry in the PAT MSR, which
we set to the Write-Combine mode on each CPU at boot time.
This commit is contained in:
Tom 2022-01-25 17:35:34 -07:00 committed by Idan Horowitz
parent 1abbe9b02c
commit 6e46e21c42
6 changed files with 40 additions and 0 deletions

View file

@ -58,6 +58,7 @@ enum class CPUFeature : u32 {
FXSR = (1 << 23), FXSR = (1 << 23),
LM = (1 << 24), LM = (1 << 24),
HYPERVISOR = (1 << 25), HYPERVISOR = (1 << 25),
PAT = (1 << 26),
}; };
} }

View file

@ -95,6 +95,7 @@ public:
UserSupervisor = 1 << 2, UserSupervisor = 1 << 2,
WriteThrough = 1 << 3, WriteThrough = 1 << 3,
CacheDisabled = 1 << 4, CacheDisabled = 1 << 4,
PAT = 1 << 7,
Global = 1 << 8, Global = 1 << 8,
NoExecute = 0x8000000000000000ULL, NoExecute = 0x8000000000000000ULL,
}; };
@ -120,6 +121,9 @@ public:
bool is_execute_disabled() const { return (raw() & NoExecute) == NoExecute; } bool is_execute_disabled() const { return (raw() & NoExecute) == NoExecute; }
void set_execute_disabled(bool b) { set_bit(NoExecute, b); } 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; } bool is_null() const { return m_raw == 0; }
void clear() { m_raw = 0; } void clear() { m_raw = 0; }

View file

@ -39,6 +39,7 @@ struct ProcessorMessageEntry;
# define MSR_GS_BASE 0xc0000101 # define MSR_GS_BASE 0xc0000101
#endif #endif
#define MSR_IA32_EFER 0xc0000080 #define MSR_IA32_EFER 0xc0000080
#define MSR_IA32_PAT 0x277
// FIXME: Find a better place for these // FIXME: Find a better place for these
extern "C" void thread_context_first_enter(void); extern "C" void thread_context_first_enter(void);

View file

@ -114,6 +114,8 @@ UNMAP_AFTER_INIT void Processor::cpu_detect()
if ((family == 6 && model >= 3) || (family == 0xf && model >= 0xe)) if ((family == 6 && model >= 3) || (family == 0xf && model >= 0xe))
set_feature(CPUFeature::CONSTANT_TSC); set_feature(CPUFeature::CONSTANT_TSC);
} }
if (processor_info.edx() & (1 << 16))
set_feature(CPUFeature::PAT);
u32 max_extended_leaf = CPUID(0x80000000).eax(); 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); 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)) { if (has_feature(CPUFeature::SMEP)) {
// Turn on CR4.SMEP // Turn on CR4.SMEP
write_cr4(read_cr4() | 0x100000); write_cr4(read_cr4() | 0x100000);
@ -309,6 +323,8 @@ NonnullOwnPtr<KString> Processor::features_string() const
return "hypervisor"sv; return "hypervisor"sv;
// no default statement here intentionally so that we get // no default statement here intentionally so that we get
// a warning if a new feature is forgotten to be added here // a warning if a new feature is forgotten to be added here
case CPUFeature::PAT:
return "pat"sv;
} }
// Shouldn't ever happen // Shouldn't ever happen
return "???"sv; return "???"sv;

View file

@ -212,6 +212,8 @@ bool Region::map_individual_page_impl(size_t page_index)
pte->set_writable(is_writable()); pte->set_writable(is_writable());
if (Processor::current().has_feature(CPUFeature::NX)) if (Processor::current().has_feature(CPUFeature::NX))
pte->set_execute_disabled(!is_executable()); 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); pte->set_user_allowed(user_allowed);
} }
return true; return true;
@ -311,6 +313,18 @@ void Region::remap()
TODO(); TODO();
} }
ErrorOr<void> 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() void Region::clear_to_zero()
{ {
VERIFY(vmobject().is_anonymous()); VERIFY(vmobject().is_anonymous());

View file

@ -87,6 +87,9 @@ public:
[[nodiscard]] bool is_mmap() const { return m_mmap; } [[nodiscard]] bool is_mmap() const { return m_mmap; }
void set_mmap(bool mmap) { m_mmap = mmap; } void set_mmap(bool mmap) { m_mmap = mmap; }
[[nodiscard]] bool is_write_combine() const { return m_write_combine; }
ErrorOr<void> set_write_combine(bool);
[[nodiscard]] bool is_user() const { return !is_kernel(); } [[nodiscard]] bool is_user() const { return !is_kernel(); }
[[nodiscard]] bool is_kernel() const { return vaddr().get() < USER_RANGE_BASE || vaddr().get() >= kernel_mapping_base; } [[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_stack : 1 { false };
bool m_mmap : 1 { false }; bool m_mmap : 1 { false };
bool m_syscall_region : 1 { false }; bool m_syscall_region : 1 { false };
bool m_write_combine : 1 { false };
IntrusiveRedBlackTreeNode<FlatPtr, Region, RawPtr<Region>> m_tree_node; IntrusiveRedBlackTreeNode<FlatPtr, Region, RawPtr<Region>> m_tree_node;
IntrusiveListNode<Region> m_vmobject_list_node; IntrusiveListNode<Region> m_vmobject_list_node;