/** * Page table structures. */ module hulk.pagetable; /** Page table entry attributes. @{ */ enum ulong PT_PRESENT = 0x1u; enum ulong PT_WRITABLE = 0x2u; enum ulong PT_USER = 0x4u; enum ulong PT_WRITE_THROUGH = 0x8u; enum ulong PT_DISABLE_CACHE = 0x10u; enum ulong PT_ACCESSED = 0x20u; enum ulong PT_DIRTY = 0x40u; enum ulong PT_PAT = 0x80u; /* Page table entry bit. */ enum ulong PT_HUGE_PAGE = 0x80u; /* Page directory entry bit. */ enum ulong PT_GLOBAL = 0x100u; enum ulong PT_NO_EXECUTE = 0x8000_0000_0000_0000u; /** @} */ /** Page size in bytes. */ enum ulong PAGE_SIZE = 4096u; /** * Structure that represents a page table entry. */ struct PageTableEntry { /** The raw page table entry is a 64-bit ulong. */ public ulong entry; alias entry this; /** * Construct a page table entry. * * @param address * Address pointed to by the page table entry (ulong or pointer). * @param flags * Page table flags (ORed set of PT_*). */ this(T)(T address, ulong flags = 0u) { entry = cast(ulong)address | flags; } /** * Return whether the page is present. */ public @property bool present() const { return (entry & PT_PRESENT) != 0u; } /** * Return whether the page is writable. */ public @property bool writable() const { return (entry & PT_WRITABLE) != 0u; } /** * Return whether the page is a user page. */ public @property bool user() const { return (entry & PT_USER) != 0u; } /** * Return whether the page is write-through. */ public @property bool write_through() const { return (entry & PT_WRITE_THROUGH) != 0u; } /** * Return whether the page has disable cache set. */ public @property bool disable_cache() const { return (entry & PT_DISABLE_CACHE) != 0u; } /** * Return whether the page is huge. */ public @property bool huge() const { return (entry & PT_HUGE_PAGE) != 0u; } /** * Return whether the page is global. */ public @property bool global() const { return (entry & PT_GLOBAL) != 0u; } /** * Return whether the page is no-execute. */ public @property bool no_execute() const { return (entry & PT_NO_EXECUTE) != 0u; } /** * Return whether the page is dirty. */ public @property bool dirty() const { return (entry & PT_DIRTY) != 0u; } /** * Return whether the page is accessed. */ public @property bool accessed() const { return (entry & PT_ACCESSED) != 0u; } /** * Follow the page table entry to the next page table it points to. */ public PageTable * follow() const { return cast(PageTable *)(entry & 0x7FFF_FFFF_FFFF_F000u); } } static assert(PageTableEntry.sizeof == 8u); /** * Structure that represents a page table. */ struct PageTable { /** Number of page table entries in a page table. */ enum size_t N_ENTRIES = 512u; /** Page table entries. */ private PageTableEntry[N_ENTRIES] entries; /** * Access the PageTableEntry for the given address and page table level. * * @param address * Address to look up the page table entry (ulong or pointer). * @param level * Page table level (0-4). */ public PageTableEntry * entry(T)(T address, ulong level) { return &entries[index(cast(ulong)address, level)]; } /** * Determine the index into the page table for the given address and page * table level. * * @param address * Address to look up the page table entry (ulong or pointer). * @param level * Page table level (0-4). */ public ulong index(T)(T address, ulong level) { return (cast(ulong)address >> (39u - (9u * level))) & 0x1FFu; } /** * Access a page table entry by linear index. * * @param index * Page table index (0-511). * * @return PageTableEntry at the given index. */ public PageTableEntry opIndex(size_t index) { return entries[index]; } } static assert(PageTable.sizeof == 4096u); /** * Get the base address of the page containing the given address. */ T page_address(T)(T address) { return cast(T)(cast(ulong)address & ~(PAGE_SIZE - 1u)); }