/* * hal_gpt.c - implementation of the Generic Page Table API for x86_64 * * Copyright (c) 2017 Maxime Villard * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include /* XXX */ #include #include #include #include #include #include #include #include #include #include #include #include extern vaddr_t __kernel_end; size_t kimg_size __in_kdata = 0; paddr_t pa_avail __in_kdata = 0; vaddr_t va_avail __in_kdata = 0; vaddr_t tmpva __in_kdata = (KERNBASE + NKL2_KIMG_ENTRIES * NBPD_L2); paddr_t hal_gpt_bootstrap_palloc(size_t npages) { paddr_t pa = pa_avail; pa_avail += npages * PAGE_SIZE; return pa; } vaddr_t hal_gpt_bootstrap_valloc(size_t npages) { vaddr_t va = va_avail; va_avail += npages * PAGE_SIZE; return va; } /* * Reset the bootstrap VA we've used in cluster0 so far. After this * function, cluster0's heap is empty. */ void hal_gpt_bootstrap_reset() { /* * Re-enter cluster0's space, because we altered it when mapping the ACPI * tables. */ hal_gpt_enter_range(CLUSTER_MIN_VA(0), 0, CLUSTER_PA_SIZE / PAGE_SIZE); hal_gpt_leave_range(CLUSTER_MIN_VA(0), (KERNTEXTOFF - KERNBASE) / PAGE_SIZE); va_avail = CLUSTER_MIN_VA(0) + KERNEL_VA_SIZE; } /* * Uniformize the PA and VA offsets, and return the value. After this function, * we are guaranteed to have [VA = PA + constant_offset]. And therefore we can * only call hal_gpt_bootstrap_valloc, without entering it in a PA. */ size_t hal_gpt_bootstrap_uniformize() { size_t pa_offset = pa_avail - 0; size_t va_offset = va_avail - CLUSTER_MIN_VA(0); if (pa_offset < va_offset) pa_avail += (va_offset - pa_offset); else if (pa_offset > va_offset) va_avail += (pa_offset - va_offset); return MAX(pa_offset, va_offset); } void hal_gpt_enter(vaddr_t va, paddr_t pa, pt_entry_t flags) { XASSERT(va % PAGE_SIZE == 0); XASSERT(pa % PAGE_SIZE == 0); //XASSERT(va == tmpva || PTE_BASE[pl1_i(va)] == 0); PTE_BASE[pl1_i(va)] = (pa & PG_FRAME) | flags; invlpg(va); } void hal_gpt_enter_range(vaddr_t va, paddr_t pa, size_t n) { pt_entry_t flags = PG_V | PG_KW | PG_NX; size_t i; for (i = 0; i < n; i++) { hal_gpt_enter(va + i * PAGE_SIZE, pa + i * PAGE_SIZE, flags); } } void hal_gpt_leave(vaddr_t va) { XASSERT(va % PAGE_SIZE == 0); XASSERT(PTE_BASE[pl1_i(va)] != 0); PTE_BASE[pl1_i(va)] = 0; invlpg(va); } void hal_gpt_leave_range(vaddr_t va, size_t n) { size_t i; for (i = 0; i < n; i++) { hal_gpt_leave(va + i * PAGE_SIZE); } } /* * Create a page tree that can map va_start->va_end. The caller can then * enter these addresses to physical locations. * * This function is a bit complicated, and may need to be revisited. */ void hal_gpt_maptree_area(vaddr_t va_start, vaddr_t va_end) { pt_entry_t flags = PG_V | PG_KW | PG_NX; size_t L4start, L4end, nL4e; size_t L3start, L3end, nL3e; size_t L2start, L2end, nL2e; paddr_t L3page, L2page, L1page; paddr_t pa; size_t i, npa; pt_entry_t *pde; /* Allocate L3 */ L4start = pl4_i(va_start); L4end = pl4_i(va_end); nL4e = (L4end - L4start + 1); L3page = hal_gpt_bootstrap_palloc(nL4e); /* Allocate L2 */ L3start = pl3_i(va_start); L3end = pl3_i(va_end); nL3e = (L3end - L3start + 1); L2page = hal_gpt_bootstrap_palloc(nL3e); /* Allocate L1 */ L2start = pl2_i(va_start); L2end = pl2_i(va_end); nL2e = (L2end - L2start + 1); L1page = hal_gpt_bootstrap_palloc(nL2e); /* Zero out L1 */ for (i = 0; i < nL2e; i++) { pa = L1page + i * PAGE_SIZE; hal_gpt_enter(tmpva, pa, flags); memset((void *)tmpva, 0, PAGE_SIZE); } /* Zero out L2 */ for (i = 0; i < nL3e; i++) { pa = L2page + i * PAGE_SIZE; hal_gpt_enter(tmpva, pa, flags); memset((void *)tmpva, 0, PAGE_SIZE); } /* Zero out L3 */ for (i = 0; i < nL4e; i++) { pa = L3page + i * PAGE_SIZE; hal_gpt_enter(tmpva, pa, flags); memset((void *)tmpva, 0, PAGE_SIZE); } /* Create L2, linked to L1 */ npa = (L2start / NPDPG) * PAGE_SIZE; for (i = L2start; i <= L2end; i++) { pa = (paddr_t)&(((pt_entry_t *)L2page)[i]); pa -= npa; /* shift on the left */ pa &= PG_FRAME; /* rounddown to a page boundary */ hal_gpt_enter(tmpva, pa, flags); pde = (pt_entry_t *)tmpva; pa = L1page + (i - L2start) * PAGE_SIZE; pde[i % NPDPG] = (pa & PG_FRAME) | PG_V | PG_KW; } /* Create L3, linked to L2 */ npa = (L3start / NPDPG) * PAGE_SIZE; for (i = L3start; i <= L3end; i++) { pa = (paddr_t)&(((pt_entry_t *)L3page)[i]); pa -= npa; /* shift on the left */ pa &= PG_FRAME; /* rounddown to a page boundary */ hal_gpt_enter(tmpva, pa, flags); pde = (pt_entry_t *)tmpva; pa = L2page + (i - L3start) * PAGE_SIZE; pde[i % NPDPG] = (pa & PG_FRAME) | PG_V | PG_KW; } /* Link L3 into L4 */ for (i = 0; i < nL4e; i++) { pa = L3page + i * PAGE_SIZE; L4_BASE[L4start + i] = (pa & PG_FRAME) | PG_V | PG_KW; } } void hal_gpt_init(paddr_t firstpa) { /* Initialize global values */ pa_avail = firstpa; va_avail = CLUSTER_MIN_VA(0) + KERNEL_VA_SIZE; kimg_size = ((uint64_t)&__kernel_end - KERNBASE); XASSERT(kimg_size % PAGE_SIZE == 0); /* * Create cluster0's page tree, enter the space, and unmap the area * below the kernel. */ hal_gpt_maptree_area(CLUSTER_MIN_VA(0), CLUSTER_MIN_VA(0) + CLUSTER_PA_SIZE); hal_gpt_enter_range(CLUSTER_MIN_VA(0), 0, CLUSTER_PA_SIZE / PAGE_SIZE); hal_gpt_leave_range(CLUSTER_MIN_VA(0), (KERNTEXTOFF - KERNBASE) / PAGE_SIZE); } /* -------------------------------------------------------------------------- */ static uint32_t hal_gpt_pte_to_attr(pt_entry_t *pte) { uint32_t attr = 0; if (*pte & PG_V) attr |= (GPT_MAPPED | GPT_READABLE); if (*pte & PG_RW) attr |= GPT_WRITABLE; if (!(*pte & PG_NX)) attr |= GPT_EXECUTABLE; if (*pte & PG_u) attr |= GPT_USER; if (!(*pte & PG_PS)) attr |= GPT_SMALL; return attr; } static pt_entry_t hal_gpt_attr_to_pte(uint32_t attr) { pt_entry_t pte = 0; if (attr & GPT_MAPPED) pte |= PG_V; if (attr & GPT_WRITABLE) pte |= PG_RW; if (!(attr & GPT_EXECUTABLE)) pte |= PG_NX; if (attr & GPT_USER) pte |= PG_u; return pte; } error_t hal_gpt_create(gpt_t *gpt) { page_t *page; xptr_t page_xp; /* check page size */ if (CONFIG_PPM_PAGE_SIZE != 4096) { panic("for x86, the page must be 4 Kbytes"); } /* allocate a physical page for L4 */ kmem_req_t req; req.type = KMEM_PAGE; req.size = 1; req.flags = AF_KERNEL | AF_ZERO; page = (page_t *)kmem_alloc(&req); if (page == NULL) { printk("\n[ERROR] in %s : cannot allocate physical memory for PT1\n", __FUNCTION__); return ENOMEM; } page_xp = XPTR(local_cxy, page); /* populate the kernel entries */ pt_entry_t *L4src, *L4dst; extern paddr_t L4paddr; // XXX XXX smp vaddr_t L4vaddr = L4paddr + KERNBASE; // XXX L4src = (pt_entry_t *)L4vaddr; L4dst = (pt_entry_t *)ppm_page2base(page_xp); memcpy(&L4dst[256], &L4src[256], 256 * sizeof(pt_entry_t)); L4dst[L4_SLOT_PTE] = (ppm_page2ppn(page_xp) << CONFIG_PPM_PAGE_SHIFT) | PG_V | PG_KW | PG_NX; /* initialize generic page table descriptor */ gpt->ptr = GET_PTR(ppm_page2base(page_xp)); gpt->ppn = ppm_page2ppn(page_xp); gpt->page = GET_PTR(page_xp); return 0; } void hal_gpt_destroy( gpt_t * gpt ) { x86_panic((char *)__func__); } void hal_gpt_print( gpt_t * gpt , pid_t pid ) { x86_panic((char *)__func__); } error_t hal_gpt_set_pte(gpt_t *gpt, vpn_t vpn, uint32_t attr, ppn_t ppn) { vaddr_t va = vpn << CONFIG_PPM_PAGE_SHIFT; paddr_t pa; kmem_req_t req; page_t *page; xptr_t page_xp; req.type = KMEM_PAGE; req.size = 0; req.flags = AF_KERNEL | AF_ZERO; if (!(attr & GPT_MAPPED)) x86_panic("hal_gpt_set_pte: unmapped!"); if (!(attr & GPT_USER)) x86_panic("hal_gpt_set_pte: kernel!"); if (!(attr & GPT_SMALL)) x86_panic("hal_gpt_set_pte: large!"); if (!(L4_BASE[pl4_i(va)] & PG_V)) { /* if L4 is not present, create it */ page = (page_t *)kmem_alloc(&req); if (page == NULL) { x86_panic("out of memory in hal_gpt_set_pte (L4)!"); return ENOMEM; } page_xp = XPTR(local_cxy, page); pa = (paddr_t)PADDR(local_cxy, ppm_page2base(page_xp)); L4_BASE[pl4_i(va)] = pa | PG_V | PG_u | PG_RW; } if (!(L3_BASE[pl3_i(va)] & PG_V)) { /* if L3 is not present, create it */ page = (page_t *)kmem_alloc(&req); if (page == NULL) { x86_panic("out of memory in hal_gpt_set_pte (L3)!"); return ENOMEM; } page_xp = XPTR(local_cxy, page); pa = (paddr_t)PADDR(local_cxy, ppm_page2base(page_xp)); L3_BASE[pl3_i(va)] = pa | PG_V | PG_u | PG_RW; } if (!(L2_BASE[pl2_i(va)] & PG_V)) { /* if L2 is not present, create it */ page = (page_t *)kmem_alloc(&req); if (page == NULL) { x86_panic("out of memory in hal_gpt_set_pte (L2)!"); return ENOMEM; } page_xp = XPTR(local_cxy, page); pa = (paddr_t)PADDR(local_cxy, ppm_page2base(page_xp)); L2_BASE[pl2_i(va)] = pa | PG_V | PG_u | PG_RW; } pa = ppn << CONFIG_PPM_PAGE_SHIFT; L1_BASE[pl1_i(va)] = pa | hal_gpt_attr_to_pte(attr); return 0; } void hal_gpt_get_pte(gpt_t *gpt, vpn_t vpn, uint32_t *attr, ppn_t *ppn) { vaddr_t va = vpn << CONFIG_PPM_PAGE_SHIFT; *attr = 0; if (!(L4_BASE[pl4_i(va)] & PG_V)) { return; } if (!(L3_BASE[pl3_i(va)] & PG_V)) { return; } if (!(L2_BASE[pl2_i(va)] & PG_V)) { return; } if (L2_BASE[pl2_i(va)] & PG_PS) { /* large page */ *attr = hal_gpt_pte_to_attr(&L2_BASE[pl2_i(va)]); *ppn = (L2_BASE[pl2_i(va)] & PG_2MFRAME) >> CONFIG_PPM_PAGE_SHIFT; } else { /* small page */ *attr = hal_gpt_pte_to_attr(&L1_BASE[pl1_i(va)]); *ppn = (L1_BASE[pl1_i(va)] & PG_FRAME) >> CONFIG_PPM_PAGE_SHIFT; } x86_panic((char *)__func__); } void hal_gpt_reset_pte( gpt_t * gpt, vpn_t vpn ) { x86_panic((char *)__func__); } error_t hal_gpt_lock_pte( gpt_t * gpt, vpn_t vpn ) { x86_panic((char *)__func__); return 0; } error_t hal_gpt_unlock_pte( gpt_t * gpt, vpn_t vpn ) { x86_panic((char *)__func__); return 0; } error_t hal_gpt_copy( gpt_t * dst_gpt, gpt_t * src_gpt, bool_t cow ) { x86_panic((char *)__func__); return 0; }