diff --git a/src/hulk/hurl.d b/src/hulk/hurl.d index 89c5ef7..d96a08a 100644 --- a/src/hulk/hurl.d +++ b/src/hulk/hurl.d @@ -5,6 +5,10 @@ */ module hulk.hurl; +import hos.cpu; +import hulk.hippo; +import hos.memory; + /** HULK virtual base address. */ enum ulong HULK_VIRTUAL_BASE_ADDRESS = 0xFFFF_8000_0000_0000u; @@ -13,3 +17,108 @@ enum ulong HULK_VIRTUAL_STACK_TOP_ADDRESS = 0xFFFF_A000_0000_0000u; /** HULK virtual framebuffer address. */ enum ulong HULK_VIRTUAL_FRAMEBUFFER_ADDRESS = 0xFFFF_A000_0000_0000u; + +/** Page table entry attributes. @{ */ +enum ulong MAP_PRESENT = 0x1u; +enum ulong MAP_WRITABLE = 0x2u; +enum ulong MAP_USER = 0x4u; +enum ulong MAP_WRITE_THROUGH = 0x8u; +enum ulong MAP_DISABLE_CACHE = 0x10u; +enum ulong MAP_HUGE_PAGE = 0x20u; +enum ulong MAP_GLOBAL = 0x40u; +enum ulong MAP_NO_EXECUTE = 0x8000_0000_0000_0000u; +/** @} */ + +struct Hurl +{ + private struct PageTableEntry + { + private ulong m_entry; + + this(ulong address, ulong flags) + { + m_entry = address | flags; + } + + this(void * address, ulong flags) + { + m_entry = cast(ulong)address | flags; + } + + public @property bool present() + { + return (m_entry & MAP_PRESENT) != 0u; + } + + public PageTable * follow() + { + return cast(PageTable *)(m_entry & 0x7FFF_FFFF_FFFF_F000u); + } + } + + private struct PageTable + { + public PageTableEntry opIndex(ulong address, ulong level) + { + PageTableEntry * entries = cast(PageTableEntry *)&this; + return entries[pt_index(address, level)]; + } + + public PageTableEntry opIndex(void * address, ulong level) + { + return opIndex(cast(ulong)address, level); + } + + public PageTableEntry opIndexAssign(PageTableEntry pte, ulong address, ulong level) + { + PageTableEntry * entries = cast(PageTableEntry *)&this; + entries[pt_index(address, level)] = pte; + return pte; + } + + public PageTableEntry opIndexAssign(PageTableEntry pte, void * address, ulong level) + { + return opIndexAssign(pte, cast(ulong)address, level); + } + + private ulong pt_index(ulong address, ulong level) + { + return (address >> (39u - (9u * level))) & 0x1FFu; + } + } + + private static __gshared PageTable * m_pt_base; + + public static void initialize() + { + m_pt_base = cast(PageTable *)read_cr3(); + } + + public static void map(ulong virtual, ulong physical, ulong flags) + { + size_t last_level = ((flags & MAP_HUGE_PAGE) != 0u) ? 2u : 3u; + PageTable * pt = m_pt_base; + for (size_t level = 0; level <= last_level; level++) + { + if (level < last_level) + { + PageTableEntry entry = (*pt)[virtual, level]; + if (entry.present) + { + pt = entry.follow(); + } + else + { + PageTable * next_pt = cast(PageTable *)hippo.allocate_page(); + memset32(next_pt, 0, 4096u / 4u); + (*pt)[virtual, level] = PageTableEntry(next_pt, MAP_WRITABLE | MAP_PRESENT); + pt = next_pt; + } + } + else + { + (*pt)[virtual, level] = PageTableEntry(physical, flags | MAP_PRESENT); + } + } + } +}