83 lines
2.5 KiB
C
83 lines
2.5 KiB
C
#include <kernel/assert.h>
|
|
#include <kernel/mem.h>
|
|
#include <kernel/print.h>
|
|
#include <kernel/spin.h>
|
|
|
|
zn_status_t mem_pt_new_kernel(struct page_table* pt, enum alloc_flags flags) {
|
|
flags &= ~ALLOC_NOZERO;
|
|
|
|
phys_t addr;
|
|
zn_status_t status = mem_phys_alloc(1, flags, &addr);
|
|
if (status)
|
|
return status;
|
|
|
|
pt->root = addr;
|
|
return 0;
|
|
}
|
|
|
|
zn_status_t mem_pt_new_user(struct page_table* pt, enum alloc_flags flags) {
|
|
// TODO
|
|
return 0;
|
|
}
|
|
|
|
// Gets a reference to the PTE at the given virtual address.
|
|
// If `check_only` is set, only checks if the PTE exists,
|
|
// and doesn't allocate new levels if they don't already exist.
|
|
// If it can't allocate a page if it has to, returns `nullptr`.
|
|
static zn_status_t get_pte(struct page_table* pt, virt_t vaddr, bool is_user, bool check_only, pte_t** ret) {
|
|
pte_t* current_head = HHDM_PTR(pt->root);
|
|
size_t index = 0;
|
|
|
|
for (int8_t level = mem_num_levels() - 1; level >= 0; level--) {
|
|
const size_t addr_mask = (1 << mem_level_bits()) - 1;
|
|
const size_t addr_shift = mem_page_bits() + (mem_level_bits() * level);
|
|
const enum pte_flags level_flags = PTE_DIR | (is_user ? PTE_USER : 0);
|
|
|
|
index = (vaddr >> addr_shift) & addr_mask;
|
|
|
|
// The last level is used to access the actual PTE, so break the loop then.
|
|
// We still need to update the index beforehand, that's why we can't just end early.
|
|
if (level == 0)
|
|
break;
|
|
|
|
pte_t* pte = ¤t_head[index];
|
|
|
|
if (mem_pte_is_present(pte)) {
|
|
// Get the next level.
|
|
*pte = mem_pte_build(mem_pte_address(pte), level_flags, CACHE_NONE);
|
|
current_head = HHDM_PTR(mem_pte_address(pte));
|
|
} else {
|
|
// If the current level isn't present, we can skip the rest.
|
|
if (check_only)
|
|
return ZN_ERR_NO_MEMORY;
|
|
|
|
phys_t addr;
|
|
zn_status_t alloc_status = mem_phys_alloc(1, 0, &addr);
|
|
if (alloc_status)
|
|
return alloc_status;
|
|
|
|
*pte = mem_pte_build(addr, level_flags, CACHE_NONE);
|
|
current_head = HHDM_PTR(addr);
|
|
}
|
|
}
|
|
|
|
*ret = ¤t_head[index];
|
|
return 0;
|
|
}
|
|
|
|
zn_status_t mem_pt_map(struct page_table* pt, virt_t vaddr, phys_t paddr, enum pte_flags flags, enum cache_mode cache) {
|
|
spin_lock(&pt->lock);
|
|
|
|
pte_t* pte;
|
|
zn_status_t status = get_pte(pt, vaddr, flags & PTE_USER, false, &pte);
|
|
if (status) {
|
|
goto fail;
|
|
}
|
|
|
|
*pte = mem_pte_build(paddr, flags, cache);
|
|
|
|
fail:
|
|
spin_unlock(&pt->lock);
|
|
return status;
|
|
}
|