#include "paging.h"
#include "arch.h"
#include "util.h"
#include "fb.h"
#include "printf.h"
#include "dbg.h"

/* Структуры таблиц страниц — размещаются в BSS, должны быть выровнены по 4K */
static uint64_t pml4[512] __attribute__((aligned(4096)));
static uint64_t pdp_low[512] __attribute__((aligned(4096)));   /* тождественное отображение 0–512 ГБ */
static uint64_t pdp_mid[512] __attribute__((aligned(4096)));   /* тождественное отображение 512–1024 ГБ */
static uint64_t pdp_high[512] __attribute__((aligned(4096)));  /* верхняя половина */
static uint64_t pd_low[512] __attribute__((aligned(4096)));    /* тождественное отображение первых 1 ГБ */
static uint64_t pd_high[512] __attribute__((aligned(4096)));   /* PD верхней половины ядра */

/* PD для области 3–4 ГБ (MMIO: LAPIC, IOAPIC, конфигурация PCI и т.д.) */
static uint64_t pd_mmio[512] __attribute__((aligned(4096)));
/* Таблицы страниц с гранулярностью 4K для точечных отображений */
static uint64_t pt_lapic[512] __attribute__((aligned(4096)));  /* Таблица страниц LAPIC */
static uint64_t pt_ioapic[512] __attribute__((aligned(4096))); /* Таблица страниц IOAPIC */

/* Внешние символы из скрипта компоновщика */
extern char __text_start[], __text_end[];
extern char __rodata_start[], __rodata_end[];
extern char __data_start[], __data_end[];
extern char __bss_start[], __bss_end[];
extern char __kernel_end[];

/* Физический базовый адрес ядра (для преобразования вирт→физ после страничной адресации) */
static uint64_t kernel_phys_base;

/* Память под таблицы страниц — нужно выделять больше динамически для отображения ядра */
/* Используем дополнительные массивы с выравниванием 4K */
static uint64_t pd_kernel_extra[512] __attribute__((aligned(4096)));
static uint64_t pt_kernel[512] __attribute__((aligned(4096)));

/* Динамические таблицы для разбиения 1ГБ→2МБ→4К для UC-отображения MMIO */
static uint64_t pdpt_split_pool[2][512] __attribute__((aligned(4096)));
static uint64_t pd_split_pool[4][512] __attribute__((aligned(4096)));
static uint64_t pt_split_pool[8][512] __attribute__((aligned(4096)));
static int pdpt_split_idx;
static int pd_split_idx;
static int pt_split_idx;

static uint64_t v2p(const void *v) {
    uint64_t x = (uint64_t)(uintptr_t)v;
    if(x >= HIGHER_HALF_BASE)
        return x - HIGHER_HALF_BASE + kernel_phys_base;
    return x;
}

static void *p2v(uint64_t p) {
    if(p >= kernel_phys_base && p < HIGHER_HALF_BASE + 0x100000000ULL)
        return (void *)(uintptr_t)(p - kernel_phys_base + HIGHER_HALF_BASE);
    return (void *)(uintptr_t)p;
}

static uint64_t* alloc_pdpt(void) {
    if(pdpt_split_idx >= 2) { fb_printf("paging: OOM pdpt\n"); return NULL; }
    uint64_t *p = pdpt_split_pool[pdpt_split_idx++];
    memset(p, 0, 4096);
    return p;
}

static uint64_t* alloc_pd(void) {
    if(pd_split_idx >= 4) { fb_printf("paging: OOM pd\n"); return NULL; }
    uint64_t *p = pd_split_pool[pd_split_idx++];
    memset(p, 0, 4096);
    return p;
}

static uint64_t* alloc_pt(void) {
    if(pt_split_idx >= 8) { fb_printf("paging: OOM pt\n"); return NULL; }
    uint64_t *p = pt_split_pool[pt_split_idx++];
    memset(p, 0, 4096);
    return p;
}


/* Map one 4KB page as UC. Works for any virtual address (identity or higher-half).
 * Splits 1GB→2MB→4KB pages using pd_split_pool/pt_split_pool. */
void paging_map_page_uc(uint64_t virt, uint64_t phys) {
    uint64_t pml4_idx = (virt >> 39) & 0x1FF;
    uint64_t pdp_idx  = (virt >> 30) & 0x1FF;
    uint64_t pd_idx   = (virt >> 21) & 0x1FF;
    uint64_t pt_idx   = (virt >> 12) & 0x1FF;

    uint64_t *pdp;
    if(pml4_idx == 0) {
        pdp = pdp_low;
    } else if(pml4_idx == 256) {
        pdp = pdp_high;
    } else if(pml4_idx >= 257 && pml4_idx <= 511) {
        /* Allocate a new PDPT for higher-half addresses beyond 512GB from HH_BASE */
        if(!(pml4[pml4_idx] & PAGE_PRESENT)) {
            uint64_t *new_pdpt = alloc_pdpt();
            if(!new_pdpt) return;
            pml4[pml4_idx] = v2p(new_pdpt) | PAGE_PRESENT | PAGE_WRITE;
        }
        pdp = (uint64_t *)p2v(pml4[pml4_idx] & ~0xFFFULL);
    } else {
        dbg_printf("Paging: UC map unsupported PML4=%d\n", pml4_idx);
        return;
    }

    /* Ensure PML4 entry exists (already done for PML4[257+] above) */
    if(!(pml4[pml4_idx] & PAGE_PRESENT)) {
        if(pml4_idx == 0) {
            pml4[pml4_idx] = v2p(pdp_low) | PAGE_PRESENT | PAGE_WRITE | PAGE_USER;
        } else if(pml4_idx == 256) {
            pml4[pml4_idx] = v2p(pdp_high) | PAGE_PRESENT | PAGE_WRITE;
        }
    }

    /* 1. Split 1GB huge page → 2MB pages, or allocate new PD */
    if(pdp[pdp_idx] & PAGE_HUGE) {
        uint64_t base = pdp[pdp_idx] & ~0x3FFFFFULL;
        uint64_t *new_pd = alloc_pd();
        if(!new_pd) return;
        for(int i = 0; i < 512; i++)
            new_pd[i] = (base + ((uint64_t)i << 21)) | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE;
        pdp[pdp_idx] = v2p(new_pd) | PAGE_PRESENT | PAGE_WRITE;
    } else if(!(pdp[pdp_idx] & PAGE_PRESENT)) {
        uint64_t *new_pd = alloc_pd();
        if(!new_pd) return;
        pdp[pdp_idx] = v2p(new_pd) | PAGE_PRESENT | PAGE_WRITE;
    }

    /* Get virtual pointer to PD (convert physical addr stored in pdp back to virtual) */
    uint64_t *pd = (uint64_t *)p2v(pdp[pdp_idx] & ~0xFFFULL);

    /* 2. Split 2MB huge page → 4KB pages, or allocate new PT */
    if(pd[pd_idx] & PAGE_HUGE) {
        uint64_t base = pd[pd_idx] & ~0x1FFFFFULL;
        uint64_t *new_pt = alloc_pt();
        if(!new_pt) return;
        for(int i = 0; i < 512; i++)
            new_pt[i] = (base + ((uint64_t)i << 12)) | PAGE_PRESENT | PAGE_WRITE;
        pd[pd_idx] = v2p(new_pt) | PAGE_PRESENT | PAGE_WRITE;
    } else if(!(pd[pd_idx] & PAGE_PRESENT)) {
        uint64_t *new_pt = alloc_pt();
        if(!new_pt) return;
        pd[pd_idx] = v2p(new_pt) | PAGE_PRESENT | PAGE_WRITE;
    }

    /* Get virtual pointer to PT */
    uint64_t *pt = (uint64_t *)p2v(pd[pd_idx] & ~0xFFFULL);

    /* 3. Set 4KB page entry with UC flag */
    pt[pt_idx] = (phys & ~0xFFFULL) | PAGE_PRESENT | PAGE_WRITE | PAGE_CACHE_DISABLE;

    invlpg(virt);
}

/* Map a physical MMIO range into higher-half (virt = phys + HIGHER_HALF_BASE) */
void paging_map_mmio_range(uint64_t phys, uint32_t size) {
    uint64_t start = phys & ~0xFFFULL;
    uint64_t end = (phys + size + 0xFFF) & ~0xFFFULL;

    dbg_printf("[paging] Map MMIO HH: phys=0x%llx size=0x%x\n", (unsigned long long)phys, size);

    for(uint64_t addr = start; addr < end; addr += 0x1000) {
        uint64_t virt = addr + HIGHER_HALF_BASE;
        paging_map_page_uc(virt, addr);
    }
}

void paging_map_page(uint64_t virt, uint64_t phys, uint64_t flags) {
    /* Отображаем одну 4К страницу в таблицах.
       Используем PML4[0] для тождественного отображения. */
    uint64_t pml4_idx = (virt >> 39) & 0x1FF;
    uint64_t pdp_idx  = (virt >> 30) & 0x1FF;
    uint64_t pd_idx   = (virt >> 21) & 0x1FF;
    uint64_t pt_idx   = (virt >> 12) & 0x1FF;

    uint64_t *pdp, *pd, *pt;

    /* Находим или создаём PDPT */
    if(!(pml4[pml4_idx] & PAGE_PRESENT)) {
        /* Нужна новая PDPT. Используем предварительно выделенные по индексу. */
        if(pml4_idx == 0) {
            pml4[pml4_idx] = (uint64_t)&pdp_low | PAGE_PRESENT | PAGE_WRITE | PAGE_USER;
        } else if(pml4_idx == 256) {
            pml4[pml4_idx] = (uint64_t)&pdp_high | PAGE_PRESENT | PAGE_WRITE;
        } else {
            /* Не поддерживается — требуется динамическое выделение */
            fb_printf("paging: unsupported PML4 index %lld\n", pml4_idx);
            return;
        }
    }

    pdp = (pml4_idx == 0) ? pdp_low : pdp_high;

    /* Находим или создаём PD */
    if(!(pdp[pdp_idx] & PAGE_PRESENT)) {
        if(pml4_idx == 0) {
            pdp[pdp_idx] = (uint64_t)&pd_low | PAGE_PRESENT | PAGE_WRITE | PAGE_USER;
        } else {
            pdp[pdp_idx] = (uint64_t)&pd_high | PAGE_PRESENT | PAGE_WRITE;
        }
    }

    pd = (pml4_idx == 0) ? pd_low : pd_high;

    /* Проверяем, не гигантская ли страница 2МБ — если бит HUGE в PD, 4К не отобразить */
    if(pd[pd_idx] & PAGE_HUGE) {
        fb_printf("paging: cannot map 4K page over 2MB huge page at virt=%p\n", virt);
        return;
    }

    /* Находим или создаём PT */
    if(!(pd[pd_idx] & PAGE_PRESENT)) {
        /* Выделяем новую PT из заранее зарезервированных */
        /* Для простоты используем pt_kernel для ядра, pt_lapic/pt_ioapic для MMIO и т.д. */
        uint64_t pt_addr;
        if(virt >= 0xFFFF800000000000ULL) {
            pt_addr = (uint64_t)&pt_kernel;
        } else if(virt >= 0xFEE00000ULL && virt < 0xFEE01000ULL) {
            pt_addr = (uint64_t)&pt_lapic;
        } else if(virt >= 0xFEC00000ULL && virt < 0xFEC01000ULL) {
            pt_addr = (uint64_t)&pt_ioapic;
        } else if(virt >= 0xC0000000ULL && virt < 0x100000000ULL) {
            /* Область MMIO 3–4 ГБ — используем pd_mmio */
            pt_addr = (uint64_t)&pt_kernel;
        } else {
            /* Общий случай — используем pt_kernel */
            pt_addr = (uint64_t)&pt_kernel;
        }
        memset((void*)pt_addr, 0, 4096);
        pd[pd_idx] = pt_addr | PAGE_PRESENT | PAGE_WRITE | ((pml4_idx == 0) ? PAGE_USER : 0);
    }

    /* Получаем физический адрес PT из записи PD */
    uint64_t pt_phys = pd[pd_idx] & ~0xFFFULL;
    /* Отображаем PT для доступа; если известная — прямой доступ подходит */
    pt = (uint64_t*)(uintptr_t)pt_phys;

    /* Устанавливаем запись в PT (PTE) */
    pt[pt_idx] = (phys & ~0xFFFULL) | flags | PAGE_PRESENT;
}

void paging_init(void) {
    /* Вычисляем физический базовый адрес ядра ДО включения страничной адресации,
       чтобы paging_map_phys_uc() работала корректно после включения. */
    kernel_phys_base = (uint64_t)(uintptr_t)&__text_start & ~0x1FFFFFULL;
    dbg_printf("Paging: kernel phys base=%p\n", kernel_phys_base);

    /* Обнуляем все таблицы страниц */
    memset(pml4, 0, sizeof(pml4));
    memset(pdp_low, 0, sizeof(pdp_low));
    memset(pdp_mid, 0, sizeof(pdp_mid));
    memset(pdp_high, 0, sizeof(pdp_high));
    memset(pd_low, 0, sizeof(pd_low));
    memset(pd_high, 0, sizeof(pd_high));
    memset(pd_mmio, 0, sizeof(pd_mmio));
    memset(pt_lapic, 0, sizeof(pt_lapic));
    memset(pt_ioapic, 0, sizeof(pt_ioapic));
    memset(pt_kernel, 0, sizeof(pt_kernel));
    memset(pd_kernel_extra, 0, sizeof(pd_kernel_extra));
    dbg_printf("Paging: tables zeroed\n");

    /* Настраиваем PML4[0] -> pdp_low (тождественное отображение 0–512 ГБ) */
    pml4[0] = (uint64_t)&pdp_low | PAGE_PRESENT | PAGE_WRITE;

    /* Настраиваем PML4[1] -> pdp_mid (тождественное отображение 512–1024 ГБ) */
    pml4[1] = (uint64_t)&pdp_mid | PAGE_PRESENT | PAGE_WRITE;

    /* Настраиваем PML4[256] -> pdp_high (верхняя половина, ядро) */
    pml4[256] = (uint64_t)&pdp_high | PAGE_PRESENT | PAGE_WRITE;
    dbg_printf("Paging: PML4=%p, PDP[0]=%p, PDP[1]=%p, PDP[256]=%p\n", pml4, pdp_low, pdp_mid, pdp_high);

    /* Тождественное отображение первых 1 ГБ через гигантские страницы 2МБ: PDP[0] -> PD_low */
    /* Настраиваем PDP[0] -> pd_low */
    pdp_low[0] = (uint64_t)&pd_low | PAGE_PRESENT | PAGE_WRITE;

    /* Заполняем PD_low гигантскими страницами по 2 МБ на первые 1 ГБ (512 записей × 2 МБ = 1 ГБ) */
    for(int i = 0; i < 512; i++) {
        pd_low[i] = ((uint64_t)i << 21) | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE | PAGE_GLOBAL;
    }
    dbg_printf("Paging: 0-1GB mapped (2MB pages)\n");

    /* Тождественное отображение 1–64 ГБ через гигантские страницы 1 ГБ в PDP[1..63].
       Образ PE загружается по адресу 0x140000000 (5 ГБ) в OVMF.
       Некоторые PCI-устройства (например, xHCI) могут иметь MMIO BAR выше 8 ГБ,
       поэтому расширяем тождественное отображение до 64 ГБ. */
    for(int i = 1; i < 64; i++) {
        pdp_low[i] = ((uint64_t)i << 30) | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE;
    }
    dbg_printf("Paging: 1-64GB mapped (1GB pages)\n");

    /* Тождественное отображение 512–1024 ГБ через гигантские страницы 1 ГБ в pdp_mid (PML4[1]).
       Прошивка часто размещает 64-битные BAR PCIe MMIO выше 4 ГБ;
       QEMU/OVMF может поместить BAR xHCI на ~768 ГБ. */
    for(int i = 0; i < 512; i++) {
        pdp_mid[i] = ((uint64_t)(512 + i) << 30) | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE;
    }
    dbg_printf("Paging: 512-1024GB mapped (1GB pages)\n");

    /* Настраиваем MSR PAT:
        PAT0 = WB (0x06) — по умолчанию
        PAT1 = WC (0x01) — write-combining для буфера кадров
        PAT2 = UC (0x00) — uncacheable для MMIO
        PAT3 = UC- (0x07) — по умолчанию

        Страницы буфера кадров: PWT=1 (PCD=0) → индекс 1 → PAT1 = WC
        Страницы MMIO: PCD=1 (PWT=0) → индекс 2 → PAT2 = UC */
    {
        uint64_t pat = rdmsr(0x277);
        pat &= ~(0xFFULL << 8);   /* Очищаем запись 1 */
        pat |= (0x01ULL << 8);    /* PAT1 = WC (write-combining) */
        pat &= ~(0xFFULL << 16);  /* Очищаем запись 2 */
        pat |= (0x00ULL << 16);   /* PAT2 = UC (uncacheable) */
        wrmsr(0x277, pat);
    }
    dbg_printf("Paging: PAT configured (WC for fb, UC for MMIO)\n");

    /* Динамически настраиваем кэширование буфера кадров: если fb.base в диапазоне 0–64 ГБ,
       разбиваем область 1 ГБ на страницы 2 МБ и ставим WC для страниц буфера кадров.
       WC позволяет буферизовать записи (вместо отправки каждой напрямую на PCI),
       что ускоряет fb_clear / fb_putchar в 10–100 раз. */
    if(fb.base && fb.base < (64ULL << 30)) {
        uint64_t fb_phys = fb.base;
        uint64_t fb_end = fb_phys + fb.pitch * fb.height;
        uint64_t fb_1gb_idx = fb_phys >> 30;

        if(fb_1gb_idx == 0) {
            /* Область 0–1 ГБ: уже использует pd_low (страницы 2 МБ). */
            for(int i = 0; i < 512; i++) {
                uint64_t page_phys = (uint64_t)i << 21;
                if(page_phys >= fb_phys && page_phys < fb_end) {
                    pd_low[i] &= ~PAGE_CACHE_DISABLE;
                    pd_low[i] |= PAGE_WRITETHROUGH; /* PWT=1 → PAT[1] = WC (write-combining) */
                }
            }
        } else if(fb_1gb_idx < 64) {
            /* Разбиваем область 1 ГБ на страницы 2 МБ */
            static uint64_t pd_fb_region[512] __attribute__((aligned(4096)));
            memset(pd_fb_region, 0, sizeof(pd_fb_region));
            uint64_t region_base = fb_1gb_idx << 30;

            pdp_low[fb_1gb_idx] = (uint64_t)&pd_fb_region | PAGE_PRESENT | PAGE_WRITE;

            for(int i = 0; i < 512; i++) {
                uint64_t page_phys = region_base + ((uint64_t)i << 21);
                uint64_t flags = PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE;
                if(page_phys >= fb_phys && page_phys < fb_end) {
                    flags |= PAGE_WRITETHROUGH; /* PWT=1 → PAT[1] = WC (write-combining) */
                }
                pd_fb_region[i] = page_phys | flags;
            }
        }
    }
    dbg_printf("Paging: fb cache=%p\n", fb.base);

    /* Отображаем ядро в верхней половине: PML4[256] -> PDP_high[0] -> PD_high */
    pdp_high[0] = (uint64_t)&pd_high | PAGE_PRESENT | PAGE_WRITE;

    /* Отображаем текст/данные/BSS ядра в верхней половине страницами по 2 МБ */
    uint64_t kernel_phys = (uint64_t)&__text_start & ~0x1FFFFFULL; /* 2MB aligned */
    uint64_t kernel_end = (uint64_t)&__kernel_end;
    uint64_t kernel_size = ((kernel_end - kernel_phys + 0x1FFFFF) & ~0x1FFFFFULL);

    fb_printf("paging: kernel at phys %p, size %llu KB\n", kernel_phys, kernel_size / 1024);

    for(uint64_t offset = 0; offset < kernel_size; offset += 0x200000) {
        uint64_t pd_entry = (offset >> 21) & 0x1FF;
        pd_high[pd_entry] = (kernel_phys + offset) | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE | PAGE_GLOBAL;
    }
    dbg_printf("Paging: kernel mapped (%dKB)\n", kernel_size / 1024);

    /* Тождественное отображение MMIO 3–4 ГБ (PDP[3]).
       Разбиваем гигантскую страницу 1 ГБ на страницы 2 МБ, затем
       отображаем LAPIC (0xFEE00000) и IOAPIC (0xFEC00000) как UC через 4K таблицы. */

    /* Проверяем, что PDP[3] — гигантская страница (задали выше), заменяем на PD */
    uint64_t mmio_base = 3ULL << 30; /* 0xC0000000 */

    /* Заполняем pd_mmio гигантскими страницами 2 МБ для всего диапазона 3–4 ГБ.
        UC через PCD=1 → индекс PAT 2 → PAT[2]=UC. */
    for(int i = 0; i < 512; i++) {
        pd_mmio[i] = (mmio_base + ((uint64_t)i << 21)) | PAGE_PRESENT | PAGE_WRITE | PAGE_HUGE | PAGE_CACHE_DISABLE;
    }

    /* Заменяем гигантскую страницу 1ГБ в PDP[3] ссылкой на pd_mmio */
    pdp_low[3] = (uint64_t)&pd_mmio | PAGE_PRESENT | PAGE_WRITE;

    /* Тождественное отображение LAPIC по адресу 0xFEE00000 (4K страница, UC).
       Индекс PD для 0xFEE00000: (0xFEE00000 >> 21) & 0x1FF = 0x1F7 = 503 */
    {
        uint64_t lapic_phys = 0xFEE00000ULL;
        uint64_t lapic_pd_idx = (lapic_phys >> 21) & 0x1FF;
        uint64_t lapic_2mb_base = lapic_phys & ~0x1FFFFFULL;

        /* Заменяем гигантскую страницу 2 МБ на таблицу 4 КБ */
        pd_mmio[lapic_pd_idx] = (uint64_t)&pt_lapic | PAGE_PRESENT | PAGE_WRITE;

        for(int i = 0; i < 512; i++) {
            uint64_t page_phys = lapic_2mb_base + ((uint64_t)i << 12);
            uint64_t flags = PAGE_PRESENT | PAGE_WRITE;
            if(page_phys >= 0xFEE00000ULL && page_phys < 0xFEE01000ULL) {
                flags |= PAGE_CACHE_DISABLE; /* UC для LAPIC */
            }
            pt_lapic[i] = page_phys | flags;
        }
    }

    /* Тождественное отображение IOAPIC по адресу 0xFEC00000 (4K страница, UC).
       Индекс PD: (0xFEC00000 >> 21) & 0x1FF = 0x1F6 = 502 */
    {
        uint64_t ioapic_phys = 0xFEC00000ULL;
        uint64_t ioapic_pd_idx = (ioapic_phys >> 21) & 0x1FF;
        uint64_t ioapic_2mb_base = ioapic_phys & ~0x1FFFFFULL;

        pd_mmio[ioapic_pd_idx] = (uint64_t)&pt_ioapic | PAGE_PRESENT | PAGE_WRITE;

        for(int i = 0; i < 512; i++) {
            uint64_t page_phys = ioapic_2mb_base + ((uint64_t)i << 12);
            uint64_t flags = PAGE_PRESENT | PAGE_WRITE;
            if(page_phys >= 0xFEC00000ULL && page_phys < 0xFEC01000ULL) {
                flags |= PAGE_CACHE_DISABLE; /* UC для IOAPIC */
            }
            pt_ioapic[i] = page_phys | flags;
        }
    }

    fb_printf("paging: page tables initialized\n");
    dbg_printf("Paging: MMIO 3-4GB configured\n");
}

void paging_enable(void) {
    /* Записываем физический адрес PML4 в CR3 */
    write_cr3((uint64_t)&pml4);

    /* Включаем PAE в CR4 */
    uint64_t cr4 = read_cr4();
    cr4 |= (1ULL << 5); /* PAE (Physical Address Extension) */
    write_cr4(cr4);

    /* Включаем LME (Long Mode Enable) в MSR EFER */
    wrmsr(0xC0000080, rdmsr(0xC0000080) | (1ULL << 8));

    /* Включаем страничную адресацию (бит PG) и защиту (PE + WP) в CR0 */
    uint64_t cr0 = read_cr0();
    cr0 |= (1ULL << 31); /* PG */
    cr0 &= ~(1ULL << 30); /* Сбрасываем CD (Cache Disable) — включаем кэш */
    write_cr0(cr0);

    fb_printf("paging: enabled (CR0=%p, CR3=%p, CR4=%p)\n", read_cr0(), read_cr3(), read_cr4());
    dbg_printf("Paging: paging enabled\n");
}
