/*
 * @author  xamidev <xamidev@riseup.net>
 * @brief   x64 4-level paging implementation
 * @license GPL-3.0-only
 */

#ifndef PAGING_H
#define PAGING_H

#define PAGE_SIZE 4096

#include <stdint.h>
#include <limine.h>
#include <mem/kheap.h>
#include <kernel.h>

void paging_init(struct boot_context boot_ctx);
void paging_map_page(uint64_t* root_table, uint64_t virt, uint64_t phys, uint64_t flags);
uint64_t* alloc_page_table();

// To swap root page tables
void load_cr3(uint64_t value);
void invlpg(void *addr);

extern uint64_t hhdm_off;

#define PHYS_TO_VIRT(x) ((void*)((uintptr_t)(x) + hhdm_off))
#define VIRT_TO_PHYS(x) ((uintptr_t)(x) - hhdm_off)

#define PTE_ADDR_MASK 0x000FFFFFFFFFF000
// Stole it
#define ALIGN_UP(x, align)   (((x) + ((align) - 1)) & ~((align) - 1))
#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1))
#define PAGE_ALIGN_DOWN(x) ((x) & PTE_ADDR_MASK)

#define ALIGN(size) ALIGN_UP(size, 16)
#define BLOCK_MIN_SIZE (sizeof(struct heap_block) + 16)

#define PML4_INDEX(x) (((x) >> 39) & 0x1FF)
#define PDPT_INDEX(x) (((x) >> 30) & 0x1FF)
#define PD_INDEX(x)   (((x) >> 21) & 0x1FF)
#define PT_INDEX(x)   (((x) >> 12) & 0x1FF)

// Page entry special bits
// Bits set on a parent (directory, table) fall back to their children
enum PTE_FLAGS
{
    PTE_PRESENT = (1ULL << 0),
    PTE_WRITABLE = (1ULL << 1),
    PTE_USER = (1ULL << 2),
    PTE_PWT = (1ULL << 3),
    PTE_PCD = (1ULL << 4),
    PTE_HUGE = (1ULL << 7),
    PTE_NOEXEC = (1ULL << 63)
};

#endif