Kernel: Implement WriteCombining memory

This makes framebuffer much faster on real hardware
This commit is contained in:
Bananymous 2024-07-15 22:08:20 +03:00
parent 42c3fa24f0
commit 0578d41500
8 changed files with 64 additions and 20 deletions

View File

@ -20,6 +20,7 @@ namespace Kernel
static PageTable* s_kernel = nullptr; static PageTable* s_kernel = nullptr;
static bool s_has_nxe = false; static bool s_has_nxe = false;
static bool s_has_pge = false; static bool s_has_pge = false;
static bool s_has_pat = false;
static paddr_t s_global_pdpte = 0; static paddr_t s_global_pdpte = 0;
@ -32,8 +33,6 @@ namespace Kernel
result |= Flags::Execute; result |= Flags::Execute;
if (entry & Flags::Reserved) if (entry & Flags::Reserved)
result |= Flags::Reserved; result |= Flags::Reserved;
if (entry & Flags::CacheDisable)
result |= Flags::CacheDisable;
if (entry & Flags::UserSupervisor) if (entry & Flags::UserSupervisor)
result |= Flags::UserSupervisor; result |= Flags::UserSupervisor;
if (entry & Flags::ReadWrite) if (entry & Flags::ReadWrite)
@ -51,6 +50,9 @@ namespace Kernel
if (CPUID::has_pge()) if (CPUID::has_pge())
s_has_pge = true; s_has_pge = true;
if (CPUID::has_pat())
s_has_pat = true;
ASSERT(s_kernel == nullptr); ASSERT(s_kernel == nullptr);
s_kernel = new PageTable(); s_kernel = new PageTable();
ASSERT(s_kernel); ASSERT(s_kernel);
@ -82,6 +84,17 @@ namespace Kernel
); );
} }
if (s_has_pat)
{
asm volatile(
"movl $0x277, %%ecx;"
"rdmsr;"
"movw $0x0401, %%dx;"
"wrmsr;"
::: "eax", "ecx", "edx", "memory"
);
}
// enable write protect // enable write protect
asm volatile( asm volatile(
"movl %%cr0, %%eax;" "movl %%cr0, %%eax;"
@ -316,7 +329,7 @@ namespace Kernel
unmap_page(page * PAGE_SIZE); unmap_page(page * PAGE_SIZE);
} }
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags) void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags, MemoryType memory_type)
{ {
ASSERT(vaddr); ASSERT(vaddr);
ASSERT(vaddr != fast_page()); ASSERT(vaddr != fast_page());
@ -338,8 +351,14 @@ namespace Kernel
extra_flags |= 1ull << 63; extra_flags |= 1ull << 63;
if (flags & Flags::Reserved) if (flags & Flags::Reserved)
extra_flags |= Flags::Reserved; extra_flags |= Flags::Reserved;
if (flags & Flags::CacheDisable)
extra_flags |= Flags::CacheDisable; if (s_has_pat)
{
if (memory_type == MemoryType::WriteCombining)
extra_flags |= (1ull << 7);
if (memory_type == MemoryType::WriteThrough)
extra_flags |= (1ull << 7) | (1ull << 3);
}
// NOTE: we add present here, since it has to be available in higher level structures // NOTE: we add present here, since it has to be available in higher level structures
flags_t uwr_flags = (flags & (Flags::UserSupervisor | Flags::ReadWrite)) | Flags::Present; flags_t uwr_flags = (flags & (Flags::UserSupervisor | Flags::ReadWrite)) | Flags::Present;
@ -367,7 +386,7 @@ namespace Kernel
invalidate(vaddr); invalidate(vaddr);
} }
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags) void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags, MemoryType memory_type)
{ {
ASSERT(vaddr); ASSERT(vaddr);
ASSERT(paddr % PAGE_SIZE == 0); ASSERT(paddr % PAGE_SIZE == 0);
@ -377,7 +396,7 @@ namespace Kernel
SpinLockGuard _(m_lock); SpinLockGuard _(m_lock);
for (size_t page = 0; page < page_count; page++) for (size_t page = 0; page < page_count; page++)
map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags); map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags, memory_type);
} }
uint64_t PageTable::get_page_data(vaddr_t vaddr) const uint64_t PageTable::get_page_data(vaddr_t vaddr) const

View File

@ -56,8 +56,6 @@ namespace Kernel
result |= Flags::Execute; result |= Flags::Execute;
if (entry & Flags::Reserved) if (entry & Flags::Reserved)
result |= Flags::Reserved; result |= Flags::Reserved;
if (entry & Flags::CacheDisable)
result |= Flags::CacheDisable;
if (entry & Flags::UserSupervisor) if (entry & Flags::UserSupervisor)
result |= Flags::UserSupervisor; result |= Flags::UserSupervisor;
if (entry & Flags::ReadWrite) if (entry & Flags::ReadWrite)
@ -106,6 +104,15 @@ namespace Kernel
); );
} }
// 64-bit always has PAT, set PAT4 = WC, PAT5 = WT
asm volatile(
"movl $0x277, %%ecx;"
"rdmsr;"
"movw $0x0401, %%dx;"
"wrmsr;"
::: "eax", "ecx", "edx", "memory"
);
// enable write protect // enable write protect
asm volatile( asm volatile(
"movq %%cr0, %%rax;" "movq %%cr0, %%rax;"
@ -367,7 +374,7 @@ namespace Kernel
unmap_page(page * PAGE_SIZE); unmap_page(page * PAGE_SIZE);
} }
void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags) void PageTable::map_page_at(paddr_t paddr, vaddr_t vaddr, flags_t flags, MemoryType memory_type)
{ {
ASSERT(vaddr); ASSERT(vaddr);
ASSERT(vaddr != fast_page()); ASSERT(vaddr != fast_page());
@ -393,8 +400,11 @@ namespace Kernel
extra_flags |= 1ull << 63; extra_flags |= 1ull << 63;
if (flags & Flags::Reserved) if (flags & Flags::Reserved)
extra_flags |= Flags::Reserved; extra_flags |= Flags::Reserved;
if (flags & Flags::CacheDisable)
extra_flags |= Flags::CacheDisable; if (memory_type == MemoryType::WriteCombining)
extra_flags |= (1ull << 7);
if (memory_type == MemoryType::WriteThrough)
extra_flags |= (1ull << 7) | (1ull << 3);
// NOTE: we add present here, since it has to be available in higher level structures // NOTE: we add present here, since it has to be available in higher level structures
flags_t uwr_flags = (flags & (Flags::UserSupervisor | Flags::ReadWrite)) | Flags::Present; flags_t uwr_flags = (flags & (Flags::UserSupervisor | Flags::ReadWrite)) | Flags::Present;
@ -434,7 +444,7 @@ namespace Kernel
invalidate(vaddr); invalidate(vaddr);
} }
void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags) void PageTable::map_range_at(paddr_t paddr, vaddr_t vaddr, size_t size, flags_t flags, MemoryType memory_type)
{ {
ASSERT(is_canonical(vaddr)); ASSERT(is_canonical(vaddr));
@ -446,7 +456,7 @@ namespace Kernel
SpinLockGuard _(m_lock); SpinLockGuard _(m_lock);
for (size_t page = 0; page < page_count; page++) for (size_t page = 0; page < page_count; page++)
map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags); map_page_at(paddr + page * PAGE_SIZE, vaddr + page * PAGE_SIZE, flags, memory_type);
} }
uint64_t PageTable::get_page_data(vaddr_t vaddr) const uint64_t PageTable::get_page_data(vaddr_t vaddr) const

View File

@ -79,5 +79,6 @@ namespace CPUID
bool is_64_bit(); bool is_64_bit();
bool has_nxe(); bool has_nxe();
bool has_pge(); bool has_pge();
bool has_pat();
} }

View File

@ -29,12 +29,17 @@ namespace Kernel
Present = (1 << 0), Present = (1 << 0),
ReadWrite = (1 << 1), ReadWrite = (1 << 1),
UserSupervisor = (1 << 2), UserSupervisor = (1 << 2),
CacheDisable = (1 << 4),
Reserved = (1 << 9), Reserved = (1 << 9),
Execute = (1 << 15), Execute = (1 << 15),
Used = Present | Reserved, Used = Present | Reserved,
}; };
enum MemoryType
{
Normal,
WriteCombining,
WriteThrough,
};
public: public:
static void initialize(); static void initialize();
@ -93,8 +98,8 @@ namespace Kernel
void unmap_page(vaddr_t); void unmap_page(vaddr_t);
void unmap_range(vaddr_t, size_t bytes); void unmap_range(vaddr_t, size_t bytes);
void map_range_at(paddr_t, vaddr_t, size_t bytes, flags_t); void map_range_at(paddr_t, vaddr_t, size_t bytes, flags_t, MemoryType = MemoryType::Normal);
void map_page_at(paddr_t, vaddr_t, flags_t); void map_page_at(paddr_t, vaddr_t, flags_t, MemoryType = MemoryType::Normal);
paddr_t physical_address_of(vaddr_t) const; paddr_t physical_address_of(vaddr_t) const;
flags_t get_page_flags(vaddr_t) const; flags_t get_page_flags(vaddr_t) const;

View File

@ -57,6 +57,13 @@ namespace CPUID
return edx & CPUID::EDX_PGE; return edx & CPUID::EDX_PGE;
} }
bool has_pat()
{
uint32_t ecx, edx;
get_features(ecx, edx);
return edx & CPUID::EDX_PAT;
}
const char* feature_string_ecx(uint32_t feat) const char* feature_string_ecx(uint32_t feat)
{ {
switch (feat) switch (feat)

View File

@ -68,7 +68,8 @@ namespace Kernel
m_video_memory_paddr & PAGE_ADDR_MASK, m_video_memory_paddr & PAGE_ADDR_MASK,
m_video_memory_vaddr, m_video_memory_vaddr,
video_memory_pages * PAGE_SIZE, video_memory_pages * PAGE_SIZE,
PageTable::Flags::ReadWrite | PageTable::Flags::Present PageTable::Flags::ReadWrite | PageTable::Flags::Present,
PageTable::WriteCombining
); );
m_video_buffer = TRY(VirtualRange::create_to_vaddr_range( m_video_buffer = TRY(VirtualRange::create_to_vaddr_range(

View File

@ -26,7 +26,7 @@ namespace Kernel
vaddr_guard.disable(); vaddr_guard.disable();
paddr_guard.disable(); paddr_guard.disable();
PageTable::kernel().map_range_at(paddr, vaddr, size, PageTable::Flags::CacheDisable | PageTable::Flags::ReadWrite | PageTable::Flags::Present); PageTable::kernel().map_range_at(paddr, vaddr, size, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
return BAN::UniqPtr<DMARegion>::adopt(region_ptr); return BAN::UniqPtr<DMARegion>::adopt(region_ptr);
} }

View File

@ -652,7 +652,8 @@ namespace Kernel::PCI
m_vaddr = PageTable::kernel().reserve_free_contiguous_pages(needed_pages, KERNEL_OFFSET); m_vaddr = PageTable::kernel().reserve_free_contiguous_pages(needed_pages, KERNEL_OFFSET);
if (m_vaddr == 0) if (m_vaddr == 0)
return BAN::Error::from_errno(ENOMEM); return BAN::Error::from_errno(ENOMEM);
PageTable::kernel().map_range_at(m_paddr, m_vaddr, m_size, PageTable::Flags::CacheDisable | PageTable::Flags::ReadWrite | PageTable::Flags::Present);
PageTable::kernel().map_range_at(m_paddr, m_vaddr, m_size, PageTable::Flags::ReadWrite | PageTable::Flags::Present);
return {}; return {};
} }